咱们都知道,在 JavaScript
里面生成一个对象有不少种方法,其中一种即是使用构造函数。首先,定义一个构造器,在构造器内部定义对象的属性,再在构造器的原型上定义对象的方法,以下所示:javascript
const Person = function (name) {
this.name = name;
}
Person.prototype.sayName = function () {
console.log(this.name)
}
复制代码
因而,当咱们对 Person
调用 new
操做符,并传入一个 name
的时候,便会生成一个新的对象, 而且该对象会继承 Person
原型上所定义的属性或方法。html
然而,当咱们的构造器拥有一个返回值的时候,会发生什么呢?java
const Person = function (name) {
this.name = name;
return { name: 'Jason' }
}
Person.prototype.sayName = function () {
console.log(this.name)
}
const person = new Person('Tony')
person.name
// Jason
person.sayName
// undefined
person instanceof Person
// false
复制代码
能够看到,当咱们在构造函数中返回一个对象时,对构造函数调用 new
操做符,最后获得的将会是咱们返回的对象,而当咱们返回一个非对象的值的时候,获得的则是在构造函数中初始化的 this
。ecmascript
注:这里的对象表示非原始值:async
The ECMAScript language types are Undefined, Null, Boolean, String, Symbol, Number, and Object.函数
那么,形成这种现象的缘由是什么呢?这个时候就须要了解当咱们对一个构造函数调用 new
操做符的时候,到底发生了什么。ui
在 ECMAScript
规范中,定义了函数对象这一律念,一个函数对象内部包含了如下几个属性(简单摘抄,完整属性列表参见前面连接):this
FunctionKind ("normal", "classConstructor", "generator", "async")
ConstructorKind("base", "derived")
做为构造器的函数对象还含有内部方法[[Construct]]
。lua
new
当咱们对一个函数调用 new
操做符的时候,会执行EvaluateNew(constructExpr, arguments)
方法。spa
constructExpr
获取相应的 constructor
constructor
不是构造器(如箭头函数)的时候,会抛出一个 TypeError
Return ? Construct(constructor, argList)
。constructor
的 construct
方法 —— Return ? F.[[Construct]](argumentsList, newTarget)
Construct
F.ConstructorKind
是否为 base
(base
表示基类),若是是 base
,则初始化函数内部的 this
为 Object.create(F.prototype)
F.ConstructorKind
是否为 base
(base
表示基类),若是是,则执行OrdinaryCallBindThis(F, calleeContext, thisArgument)
OrdinaryCallEvaluateBody(F, argumentsList)
,获得结果 result
result
的值为一个对象,则直接返回该对象F.ConstructorKind
为 base
,则返回上面初始化的 this
result
的值不是 undefined
,则抛出一个TypeError
class
的 constructor
中返回了一个字符串class A {}
class B extends A {
constructor() {
return ''
}
}
// Uncaught TypeError: Derived constructors may only return object or undefined
new B()
复制代码
在实际的使用过程当中, 咱们每每不多会在构造函数中返回一个值,最多见的场景大概是 return this
以实现链式调用。在某次突发奇想,对此感到好奇而且尝试以后,一路刨根问底,才了解到简单的调用背后,包含了这么多复杂的步骤。