由於proto而產生的instanceof問題

最近有了解下new的實現原理,發現一個關於instanceof的問題。瀏覽器

function Foo(name,age) {
    this.name = name
    this.age = age
}

function createClass(){
    const obj = Object.create(null)
    const FN = [].shift.call(arguments)
    obj.__proto__ = FN.prototype
    FN.apply(obj,arguments)
    return obj
}
let obj = createClass(Foo,'zl')
console.log(obj instanceof Foo) // false
複製代碼

我又去了解了下instanceof的實現,源碼沒找到,只是在網上找到了原理bash

function instanceofTest(left, right) {
  // 得到類型的原型
  let prototype = right.prototype
  // 得到對象的原型
  left = left.__proto__
  // 判斷對象的類型是否等於類型的原型
  while (true) {
    if (left === null)
      return false
    if (prototype === left)
      return true
    left = left.__proto__
  }
}
console.log(instanceofTest(obj, Foo) // true
複製代碼

如今問題就出現了,確定是new或者instanceof實現有問題,才致使這種狀況,最後,我上Stack Overflow請求幫助,才明白問題出在__proto__上面。做爲實例對象的一個隱藏屬性,在不一樣瀏覽器上的表現形式不同,並且,經過__proto__並無真正修改了實例對象obj的原型鏈,只是修改了原型屬性。所以,經過es5提供的標準方法來獲取真正的原型鏈上的原型會發現app

console.log(Object.getPrototypeOf(obj)) //null
複製代碼

所以,對自定義的new和instaceof進行簡單的改造便可:ui

function createClass(){
    const obj = Object.create(null)
    const FN = [].shift.call(arguments)
    Object.setPrototypeOf(obj, FN.prototype)
    FN.apply(obj,arguments)
    return obj
}
function instanceofTest(left, right) {
  // 得到類型的原型
  let prototype = right.prototype
  // 得到對象的原型
  left = Object.getPrototypeOf(left)
  // 判斷對象的類型是否等於類型的原型
  while (true) {
    if (left === null)
      return false
    if (prototype === left)
      return true
    left = Object.getPrototypeOf(left)
  }
}
console.log(obj instanceof Foo) // true
複製代碼

null 與 {}this

這一個問題的產生不是__proto__兼容問題,object.create(null)建立的對象,自己爲原型鏈頂層,再也不包含Object原型鏈上的屬性以及下一層的原型鏈。那麼使用對象屬性__proto__僅僅爲null對象的屬性賦值,並未建立原型鏈,intanceof遍歷原型鏈上找不到對應的原型。經過系統方法setPrototypeOf爲null對象賦值原型屬性__proto__,同時將null對象的原型指向該原型鏈。所以,僅僅將object.create(null) 替換爲{}一樣能夠解決instanceof失敗問題es5

相關文章
相關標籤/搜索