被要求实现instanceof,面试官到底在考我什么?

5,828 阅读3分钟

也许你我素未谋面,但很可能相见恨晚,我是前端胖头鱼

被问麻了

相信小伙伴面试时不止一次被要求手写实现xxx了,曾经胖头鱼也被问过n次类似问题,其中有一个题目映像比较深刻,instanceof实现原理,这篇文章我想和大家一起尝试用至少3种方式实现...

胖头鱼的手写实现仓库(350 star),你要的手写都有,欢迎点击看看

github.com/qianlongo/f…

用法回顾

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。MDN上

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);

console.log(auto instanceof Car);
// expected output: true

console.log(auto instanceof Object);
// expected output: true

原型链回顾

原型、原型链是实现instanceof最重要的知识点,这里简单回顾一下,推荐两篇写的很好的文章。

image.png

请看图:

1. f是Fo的实例对象,它会有一个__proto__属性指向Fo的原型prototype
2. Fo继承自Foo,其prototype是Foo的实例,会有一个__proto__属性指向Foo的的原型prototype
3. Foo继承自顶层对象Object,其prototype是Object的实例,会有一个__proto__属性指向Object.prototype
4. Object.prototype已经是最顶层的对象,其__proto__属性指向null

代码示例

const Foo = function (name, sex) {
  this.name = name
  this.sex = sex
}
Foo.prototype.showName = function () {
  console.log(this.name)
}

const Fo = function (name) {
  Foo.call(this, name)
}
Fo.prototype = Object.create(Foo.prototype)

const f = new Fo('前端胖头鱼')

f.showName() // 前端胖头鱼

image.png

三种实现方式

要实现instanceof本质上只要只要遍历实例对象的原型链,挨个往上查找看是否有与Fn的prototype相等的原型,直到最顶层Object还找不到,那么就返回false,否则结果就是true

关键点:

  1. 构造函数Fn的prototype
  2. 实例对象的原型链

递归实现(方式1)


/**
 * 
 * @param {*} obj 实例对象
 * @param {*} func 构造函数
 * @returns true false
 */
const instanceOf1 = (obj, func) => {
  // 必须是对象或者函数 
  if (!(obj && ['object', 'function'].includes(typeof obj))) {
    return false
  }

  let proto = Object.getPrototypeOf(obj)

  if (proto === func.prototype) {
    return true
  } else if (proto === null) {
    return false
  } else {
    return instanceOf1(proto, func)
  }
}

// 测试
let Fn = function () { }
let p1 = new Fn()

console.log(instanceOf1({}, Object)) // true
console.log(instanceOf1(p1, Fn)) // true
console.log(instanceOf1({}, Fn)) // false
console.log(instanceOf1(null, Fn)) // false
console.log(instanceOf1(1, Fn)) // false

遍历实现(方式2)

/**
 * 
 * @param {*} obj 实例对象
 * @param {*} func 构造函数
 * @returns true false
 */
const instanceOf2 = (obj, func) => {
  // 必须是对象或者函数 
  if (!(obj && ['object', 'function'].includes(typeof obj))) {
    return false
  }

  let proto = obj

  while (proto = Object.getPrototypeOf(proto)) {
    if (proto === func.prototype) {
      return true
    }
  }

  return false
}

// 测试
let Fn = function () { }
let p1 = new Fn()

console.log(instanceOf2({}, Object)) // true
console.log(instanceOf2(p1, Fn)) // true
console.log(instanceOf2({}, Fn)) // false
console.log(instanceOf2(null, Fn)) // false
console.log(instanceOf2(1, Fn)) // false

遍历实现(方式3)

/**
 * 
 * @param {*} obj 实例对象
 * @param {*} func 构造函数
 * @returns true false
 */
const instanceOf3 = (obj, func) => {
  // 必须是对象或者函数 
  if (!(obj && ['object', 'function'].includes(typeof obj))) {
    return false
  }

  let proto = Object.getPrototypeOf(obj)
  // 因为一定会有结束的时候(最顶层Object),所以不会是死循环
  while (true) {
    if (proto === null) {
      return false
    } else if (proto === func.prototype) {
      return true
    } else {
      proto = Object.getPrototypeOf(proto)
    }
  }
}

// 测试
let Fn = function () { }
let p1 = new Fn()

console.log(instanceOf3({}, Object)) // true
console.log(instanceOf3(p1, Fn)) // true
console.log(instanceOf3({}, Fn)) // false
console.log(instanceOf3(null, Fn)) // false
console.log(instanceOf3(1, Fn)) // false


最后

希望能一直给大家分享实用、基础、进阶的知识点,一起早早下班,快乐摸鱼。

期待你在掘金关注我:前端胖头鱼,也可以在公众号里找到我:前端胖头鱼