Skip to content

js中的instanceof

前言

我们知道js的原型链很长很长,一个对象在访问某个属性或者方法时,如果自己没有,就会去原型链上找,一层一层往上找,直到找到为止,(原型链的相关知识参考上一篇文章,原型链)。那么当我们在写代码时,假如有一个实例对象,想去调用它父亲的某个方法,但是我们并不确定它是否属于这个父亲的实例对象时,该怎么做呢?比如下面代码:

js
function Parent() { }
Parent.prototype.sayHi = function () {
    console.log('hello');
}

let son = new Parent() 
son.sayHi() // 假如我们不知道这个son是哪来的,想调用sayHi方法

在上面例子中,我们有个构造方法Parent,它的原型上有个sayHi()方法,有一个实例对象son,假如我们不能确定son对象是不是通过Parent声明出来的,如果想更安全的调用sayHi()方法,可以增加一层判断,使用instanceof,如:

js
if (son instanceof Parent) { // 使用 instanceof 判断一下
    son.sayHi()
}

instanceof的工作原理

A instanceof B,就是顺着A__proto__往上找,看看能不能找到B.prototype,如果能找到就返回true,如果找不到,就返回false

借用一个原型链中的图。

原型链

如图中的f2f1

js
function Foo() { }
let f1 = new Foo()
console.log(f1.__proto__ === Foo.prototype); // true
console.log(f1.__proto__.__proto__ === Object.prototype); // true
console.log(f1.__proto__.__proto__.__proto__ === null); // true

console.log(f1 instanceof Foo); // true
console.log(f1 instanceof Object); // true
console.log(f1 instanceof null); // TypeError: Right-hand side of 'instanceof' is not an object
  1. 因为f1.__proto__ === Foo.prototype,在f1的原型链上找到了Foo的原型,所以f1 instanceof Foo === true
  2. 因为f1.__proto__.__proto__ === Object.prototype,在f1的原型链上找到了Object的原型,所以f1 instanceof Object === true

使用typeof null 时,虽然返回值是object,但是null实际不是object,这是一个历史遗留问题,null在大多数语言中,被设计成空指针,指向了Ox00。参考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/typeof

手写一个instanceof

  1. A instanceof B,方法传入两个参数,A和B
  2. 判断A__proto__链上能不能找到B.prototype
  3. 使用一个while(true)循环,一直往上找
js
function MyInstanceof(leftValue, rightValue) {
    let rightPrototype = rightValue.prototype
    let leftProto = leftValue.__proto__

    while (true) {
        // 出口
        if (leftProto === null) {
            return false
        }

        if (leftProto === rightPrototype) {
            return true
        }

        leftProto = leftProto.__proto__
    }
}

参考

https://juejin.cn/post/6844903613584654344#heading-1