由于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

相关文章
相关标签/搜索