模擬實現js的new
阿新 • • 發佈:2020-08-11
目錄
new是什麼
一句話介紹new :new運算子建立一個使用者自定義的物件型別的例項,或者具有建構函式的內建物件型別之一。看下下面的程式碼來了解new操作符都做了什麼事情
// Class (constructor) function Person(name,age){ this.name = name this.age = age this.habit = 'watch tv' } // 每個函式都有prototype物件屬性 // 在類的原型上掛載屬性和方法,掛載載原型上,每個例項都可以呼叫,並且不會每個例項都掛載相同的屬性和方法 Person.prototype.strLength = 60 Person.prototype.sayName = function(){ console.log('I am '+ this.name) } // 例項化物件 const person = new Person("yato", 50) person.sayName(); console.log(person)
進一步理解new
從上面這個例子中,我們可以看到,例項person可以
- 訪問到Person建構函式裡的屬性
- 訪問到Person.prototype中的屬性
接下來模擬實現一個類似new的newFake
,使用方式如下
function Person(arguments){
// ...
}
// 使用new
let person = new Person(arguments)
// 使用newFake
let person = newFake(Person,arguments)
初步實現
function newFake(){ let obj = Object.create({}) let Constructor = [].shift.call(arguments) if(typeof Constructor !== 'function'){ throw 'newOperator function the first param must be a function'; } // 將新建物件的[[prototype]]屬性指向到建構函式的prototype屬性 obj.__proto__ = Constructor.prototype // 修改this指向到obj Constructor.apply(obj, arguments) return obj } function Person(name,age){ this.name = name this.age = age this.habit = 'watch tv' } // 每個函式都有prototype物件屬性 // 在類的原型上掛載屬性和方法,掛載載原型上,每個例項都可以呼叫,並且不會每個例項都掛載相同的屬性和方法 Person.prototype.strLength = 60 Person.prototype.sayName = function(){ console.log('I am '+ this.name) } // 例項化物件 const person = newFake(Person,"yato", 50) person.sayName(); // I am yato console.log(person) // Person { name: 'yato', age: 50, habit: 'watch tv' }
返回值處理
如果建構函式有返回值的情況
function Person(name,age){ this.strLength = 60 this.age = age return { name: name, habit: 'game' } } let person = new Person('yato', 18) console.log(person.name) // yato console.log(person.habit) // game console.log(person.strength) // undefined console.log(person.age) // undefined
在這個例子中,建構函式返回了一個物件,在例項person中只能訪問返回物件中的屬性,而且還要注意一點,這裡我們是返回一個物件,假設我們只返回一個基本型別值呢,看下面的例子
function Person(name,age){
this.strLength = 60
this.age = age
return 'good job'
}
let person = new Person('yato', 18)
console.log(person.name) // undefined
console.log(person.habit) // undefined
console.log(person.strength) // 60
console.log(person.age) // 18
結果和正常new例項,無返回值的時候表現是一樣的!可以得出結論:new操作最後一步,需要判斷一返回值是否是一個物件,如果是一個物件就返回這個物件,如果不是物件就返回我們new內部的例項物件
第二版的new實現
function newFake(){
let obj = Object.create({})
let Constructor = [].shift.call(arguments)
if(typeof Constructor !== 'function'){
throw 'newOperator function the first param must be a function';
}
// ES6 new.target 是指向建構函式
newFake.target = Constructor;
// 將新建物件的[[prototype]]屬性指向到建構函式的prototype屬性
obj.__proto__ = Constructor.prototype
// 修改this指向到obj
let ret = Constructor.apply(obj, arguments)
// 判斷返回值是否為物件 Object(包含Functoin, Array, Date, RegExg, Error)都會直接返回這些值。
// 這些型別中合併起來只有Object和Function兩種型別 typeof null 也是'object'所以要不等於null,排除 // null
let isObject = typeof ret === 'object' && ret !== null;
let isFunction = typeof ret === 'function';
if(isObject || isFunction){
return ret;
}
return obj
}
總結一下new做了什麼
- 建立了一個全新的物件
- 這個物件會被執行例項[[prototype]]屬性到Class的
prototype
物件屬性的連結(原型鏈) - 生成的新物件會成為建構函式的呼叫的this(修改this指向)
- 通過new建立的每個例項物件最終將被[[prototype]]連結到建構函式的prototype物件上
- 如果函式沒有返回物件型別(包含
Function
,Array
,Date
,RegExg
,Error
),那麼new表示式中的函式會自動返回這個新的物件