JavaScript中new
操作符用於創建一個給定構造函數的對象實例。如下例子:
function Person(name, age){
this.name = name;
this.age = age;
}
const person1 = new Person('Tom', 20)
console.log(person1) // Person {name: "Tom", age: 20}
我們定義了一個構造函數Person
,然後通過new
操作符生成Person構造函數的一個實例並將其引用賦值給變量person1
。然後控制檯打印出person1
的內容,可以看到該實例對象具有name
和age
屬性,它們的值就是我們在調用構造函數時傳入的值。
那麼,我們使用new
操作符的時候都發生了哪些事呢?
備註:如果對JS中的prototype
、__proto__
與constructor
屬性不大熟悉的話,強烈建議先看一下這篇文章再來看一下內容:幫你徹底搞懂JS中的prototype、__proto__與constructor(圖解)
new關鍵字進行的操作
new
關鍵字進行了如下的操作(爲了便於描述,obj
用來表示創建的空對象、用constrc
來表示構造函數):
- 創建一個空對象
obj
({}
); - 將
obj
的[[prototype]]
屬性指向構造函數constrc
的原型(即obj.[[prototype]] = constrc.prototype
)。 - 將構造函數
constrc
內部的this
綁定到新建的對象obj
,執行constrc
(也就是跟調用普通函數一樣,只是此時函數的this爲新創建的對象obj
而已,就好像執行obj.constrc()
一樣); - 若構造函數沒有返回非原始值(即不是引用類型的值),則返回該新建的對象
obj
(默認會添加return this
)。否則,返回引用類型的值。
這裏補充說明一下:[[prototype]]
屬性是隱藏的,不過目前大部分新瀏覽器實現方式是使用__proto__
來表示。構造函數的prototype
屬性我們是可以顯式訪問的。
讓我們用圖來展示文章開頭的那個例子的過程:
怎麼樣,是不是對new
操作符的執行過程有了一個清晰的瞭解了?
自己實現new操作符
從上面我們已經清楚地掌握了new
的執行過程,那麼我們就動手來自己實現一下new
操作吧!
function myNew(constrc, ...args) {
const obj = {}; // 1. 創建一個空對象
obj.__proto__ = constrc.prototype; // 2. 將obj的[[prototype]]屬性指向構造函數的原型對象
// 或者使用自帶方法:Object.setPrototypeOf(obj, constrc.prototype)
const result = constrc.apply(obj, args); // 3.將constrc執行的上下文this綁定到obj上,並執行
return result instanceof Object ? result : obj; //4. 如果構造函數返回的是對象,則使用構造函數執行的結果。否則,返回新創建的對象
}
// 使用的例子:
function Person(name, age){
this.name = name;
this.age = age;
}
const person1 = myNew(Person, 'Tom', 20)
console.log(person1) // Person {name: "Tom", age: 20}
這裏的關鍵兩步就是:
- 將新創建對象的原型鏈設置正確,這樣我們才能使用原型鏈上的方法。
- 將新創建的對象作爲構造函數執行的上下文,這樣我們才能正確地進行一些初始化操作。
本文結束!
參考文獻: