New幹了什麼
(1) 創建一個新對象;
(2) 將構造函數的作用域賦給新對象(因此 this 就指向了這個新對象) ;
(3) 執行構造函數中的代碼(為這個新對象添加屬性) ;
(4) 綁定原型;
(4) 返回新對象。
注意:
- 如果構造函數有返回的值並且為基本類型的話則沒有影響,返回值毫無意義;
- 如果返回值的類型為object,那這個返回值會被正常使用
function Test(name) {
this.name = name
return 1
}
const t = new Test('yck')
console.log(t.name) // 'yck'
function Test(name) {
this.name = name
console.log(this) // Test { name: 'yck' }
return { age: 26 }
}
const t = new Test('yck')
console.log(t) // { age: 26 }
console.log(t.name) // 'undefined'
實現方式
因為 new 是關鍵字,所以無法直接覆蓋,所以我們寫一個函數,命名為 myNew,來模擬 new 的效果。用的時候是這樣的:
function person() {
……
}
// 使用 new
var person = new person(……);
// 使用 objectFactory
var person = myNew(person, ……)
實現過程
因為 new 的結果是一個新對象,所以在模擬實現的時候,我們也要建立一個新對象,假設這個對象叫 obj,因為 obj 會具有 Otaku 構造函數裏的屬性,想想經典繼承的例子,我們可以使用 Otaku.apply(obj, arguments)來給 obj 添加新的屬性。
function myNew() {
//用new Object() 的方式新建了一個對象 obj
var obj = new Object(),
//取出傳入的構造函數。此外因為shift會修改原數組,所以arguments會被去除第一個參數
Constructor = [].shift.call(arguments);
//將 obj 的原型指向構造函數,這樣 obj 就可以訪問到構造函數原型中的屬性
obj.__proto__ = Constructor.prototype;
//使用 apply,改變構造函數 this 的指向到新建的obj對象,並執行了Constructor,這樣obj就會被添加屬性
var ret = Constructor.apply(obj, arguments);
//判斷返回的值是不是一個對象,如果是一個對象,我們就返回這個對象,如果不是,我們原樣返回
return typeof ret === 'object' ? ret : obj;
//返回出去
return obj;
};
驗證myNew
function person(name, age) {
this.name = name;
this.age = age;
this.habit = 'Games';
}
person.prototype.strength = 60;
person.prototype.sayYourName = function () {
console.log('I am ' + this.name);
}
var person = myNew(person, 'Kevin', '18')
console.log(person.name) // Kevin
console.log(person.habit) // Games
console.log(person.strength) // 60
person.sayYourName(); // I am Kevin
New的其他簡潔寫法
function myNew(Con, ...args) {
let obj = {}
//等同於 obj.__proto__ = Con.prototype
Object.setPrototypeOf(obj, Con.prototype)
let result = Con.apply(obj, args)
return result instanceof Object ? result : obj
}