咱們都知道,在 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
以實現鏈式調用。在某次突發奇想,對此感到好奇而且嘗試以後,一路刨根問底,才瞭解到簡單的調用背後,包含了這麼多複雜的步驟。