手寫:javascript中的關鍵字new
- 2020 年 9 月 24 日
- 筆記
- javascript, 手寫系列
簡單介紹一下new
new再熟悉不過了,new後面跟著構造函數,可以創建對象,這個對象的原型指向構造函數的原型對象,說起來可能有點繞,直接看程式碼吧
function Person(name, age){
this.name = name;
this.age = age;
}
let person1 = new Person("張三", 22);
console.log(person1.__proto__ === Person.prototype); // true
console.log(person1 instanceof Person); // true
而new後面的構造函數,也可以這樣調用
let person2 = new Person;
其實這樣寫就等同於,不傳任何參數調用構造函數
let person2 = new Person();
列印person2可以看到
Person { name: undefined, age: undefined }
一般情況下,構造函數是不定義返回值的,那我們試一試,如果構造函數定義返回值,會怎麼樣,首先,返回一個字元串
function Person(name, age){
this.name = name;
this.age = age;
return "person"
}
let person1 = new Person("張三", 22);
let person2 = new Person;
console.log(person1); // Person { name: '張三', age: 22 }
console.log(person2); // Person { name: undefined, age: undefined }
console.log(person1.__proto__ === Person.prototype); // true
console.log(person1 instanceof Person) // true
看來構造函數返回字元串沒有影響,那返回一個對象呢
function Person(name, age){
this.name = name;
this.age = age;
return {
id : "person"
}
}
let person1 = new Person("張三", 22);
let person2 = new Person;
console.log(person1); // { id: 'person' }
console.log(person2); // { id: 'person' }
console.log(person1.__proto__ === Person.prototype); // false
console.log(person1 instanceof Person) // false
可以看到,如果返回一個對象,那麼通過new關鍵字創建的對象則為構造返回的值,且也不繼承於構造的原型對象
手寫
了解的差不多了,開始手寫
首先定義一個函數名為myNew,參數接收一個function,也就是構造函數,
function myNew(Constructor){
if(typeof Constructor !== "function"){
throw "參數Constructor必須是方法(function)"
}
}
ok,緊接著需要創建一個新的對象,並且這個對象的原型指向的是構造函數的原型對象
所以
function myNew(Constructor){
if(typeof Constructor !== "function"){
throw "參數Constructor必須是方法(function)"
}
let obj = {};
obj.__proto__ = Constructor.prototype;
}
接著,因為執行構造函數的是,需要傳參數,並且在構造函數內部會為this賦值,所以我們需要執行構造函數,並且把構造函數內的上下文指向到obj
function myNew(Constructor){
if(typeof Constructor !== "function"){
throw "參數Constructor必須是方法(function)"
}
let obj = {};
obj.__proto__ = Constructor.prototype;
// 獲取參數
let params = Array.from(arguments);
// 把參數第一位,也就是構造函數移除
params.shift();
// 執行構造函數,並且吧上下文只設置為obj
Constructor.apply(obj, params);
return obj
}
但是我們需要前面提到了,有時候構造函數是可能有返回值的,所以最終程式碼為
最終源碼
function myNew(Constructor){
if(typeof Constructor !== "function"){
throw "參數Constructor必須是方法(function)"
}
let obj = {};
obj.__proto__ = Constructor.prototype;
// 獲取參數
let params = Array.from(arguments);
// 把參數第一位,也就是構造函數移除
params.shift();
// 執行構造函數,並且吧上下文只設置為obj
let result = Constructor.apply(obj, params);
// 判斷構造函數執行返回是不是object,是的話返回這個對象
return typeof result === "object" ? result : obj
}
我們以之前的例子來驗證一下
function Person(name, age){
this.name = name;
this.age = age;
return "person"
}
let person1 = myNew(Person, "張三", 22);
let person2 = myNew(Person);
console.log(person1); // Person { name: '張三', age: 22 }
console.log(person2); // Person { name: undefined, age: undefined }
console.log(person1.__proto__ === Person.prototype); // true
console.log(person1 instanceof Person) // true
構造有返回值的時候
function Person(name, age){
this.name = name;
this.age = age;
return {
id : "person"
}
}
let person1 = myNew(Person, "張三", 22);
let person2 = myNew(Person);
console.log(person1); // { id: 'person' }
console.log(person2); // { id: 'person' }
console.log(person1.__proto__ === Person.prototype); // false
console.log(person1 instanceof Person) // false
與前面完全一致