1. 定義
new 運算符創建一個用戶定義的對象類型的實例或具有構造函數的內置對象的實例
先來看一段代碼:
function Person(name) {
this.name = name;
}
Person.prototype.show = () => this.name;
const p1 = new Person('zhangsan');
console.log(p1); // Person {name: "zhangsan"}
// 訪問構造函數里的屬性
console.log(p1.name); // zhangsan
// 訪問構造函數原型里的方法
console.log(p1.show()); // zhangsan
// 實例原型指向構造函數的原型對象
console.log(p1.__proto__ == Person.prototype); // true
從上面代碼可以知道,實例 person 可以:
- 訪問到 Person 構造函數里的屬性
- 訪問到 Person.prototype 中的屬性
- 實例原型就是構造函數的原型對象
這個是最基本的了,也是new一個對象就知道的特點
2. 其他特點
當構造函數中沒有返回值時,new一個對象會返回一個新的對象,如果構造函數有返回值呢?
2-1. 構造函數返回基本數據類型
function Person(name) {
this.name = name;
// return 111 // number
// return '111' // string
// ...
return true // boolean
}
Person.prototype.show = () => this.name;
const p1 = new Person('zhangsan');
console.log(p1); // Person {name: "zhangsan"}
從打印結果可知:和沒有return效果一樣
2-2. 構造函數返回引用類型
function Person(name) {
this.name = name;
// return {name: 111} // object
// return [] // array
// ...
// return () => 111 // fucntion
}
Person.prototype.show = () => this.name;
const p1 = new Person('zhangsan');
console.log(p1);
// {name: 111}
// []
// () => 111
從打印結果可知:最后返回 return 后面的對象
3. new運算符原理
- 創建一個空對象
- 對象的原型指向傳人構造函數的原型對象
- 執行構造函數中的代碼,為這個新對象添加屬性
- 如果構造函數代碼執行后沒有返回值或者返回值為基礎數據類型,則返回創建的對象,否則返回執行結果
4. 模擬實現(白話說明)
function myNew() {
// 因為new出來是一個對象,所以先創建一個空的對象
const obj = new Object();
// 獲取傳入的構造函數
// 為何要操作這一步?該段代碼的精髓在于:
// 1、獲取了構造函數
// 2、修改了arguments將構造函數參數和其他參數區分了開來,為之后的執行構造函數提供了便利
const Constructor = [].shift.call(arguments); // Array.shift() 刪除數組第一個元素,修改原數組,并返回刪除的元素
// 將obj原型指向構造函數原型對象,obj 可以訪問到構造函數原型對象中的屬性
// 為何要改變原型指向?
// 打印new出來的對象的原型,你會發現該原型等同于構造函數的原型對象
// 為什么一會是.__proto__一會又是.prototype?
// 簡單說對象用.__proto__,函數用.prototype,具體講解見最后參考
obj.__proto__ = Constructor.prototype;
// 綁定 this 實現繼承,現在obj 可以訪問到構造函數中的屬性
// 該段代碼的精髓在于:
// 即為obj新增了屬性又可以拿到構造函數的返回值
// 如 this.name = name =》obj.name = name;
// 獲取構造函數返回值之后就可以用于決定我們最后的輸出
const ret = Constructor.apply(obj, arguments); // 這里就體現了[].shift.call(arguments)這段代碼的好處
// 優先返回構造函數返回的對象
// ret為構造函數的返回值,如果它為引用數據類型則直接返回,否則就返回內部創建的對象
return ret instanceof Object ? ret : obj;
}
5. 測試
function Person(name) {
this.name = name;
}
Person.prototype.show = () => this.name;
const p1 = new Person('zhangsan');
console.log(p1); // Person {name: "zhangsan"}
function myNew() {
const obj = new Object();
const Constructor = [].shift.call(arguments);
obj.__proto__ = Constructor.prototype;
const ret = Constructor.apply(obj, arguments);
return ret instanceof Object ? ret : obj;
}
const p2 = myNew(Person, 'lisi');
console.log(p2); // Person {name: "lisi"}
console.log(p1.__proto__ == p2.__proto__); // true
完工!
6. 參考
幫你徹底搞懂JS中的prototype、__proto__與constructor(圖解)
深入理解JavaScript之 new 原理及模擬實現