关于原理:JS-原生方法原理探究五如何实现-instanceof

4次阅读

共计 2213 个字符,预计需要花费 6 分钟才能阅读完成。

这是 JS 原生办法原理探索系列的第五篇文章。本文会介绍如何实现 instanceof 办法。

typeof 操作符返回一个示意数据类型的字符串,它能够应酬惯例场景下的数据类型判断。对根本数据类型 undefinedbooleanstringnumberSymbol 和援用数据类型 function 都能够正确判断,然而对 null、数组、对象等则对立返回 “object”。

比如说:

function F1(){}
function F2(){}
const obj1 = new F1()
const obj2 = new F2()
typeof obj1            //‘object’typeof obj2           // 'object' 

这里只能看出 obj1obj2 是对象,但不晓得具体是哪个构造函数创立的对象。

但应用 instanceof 之后,就高深莫测了:

console.log(obj1 instanceof F1)    // true
console.log(obj1 instanceof F2)    // false
console.log(obj2 instanceof F2)    // true

依据 MDN 的形容:

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

instanceof 运算符有两个操作数,左操作数通常是一个实例对象,它的类型能够是对象或者函数,也能够是根本类型(这种状况下不会报错,但总返回 false),右操作数通常是一个可调用的(callable)对象,咱们能够间接认为它的类型应该是一个函数。

那么 instanceof 的实现原理是什么呢?从定义中咱们能够看到,它的原理和原型链的机制无关,具体地说,它会拿到右操作数的原型对象,而后在左操作数上通过 __proto__ 一直查找实例的原型链,只有右操作数的 prototype 呈现在左操作数的原型链上时,就返回 true。如果原型链始终查找到止境 —— 也就是 null,还没有找到右操作数的原型,就返回 false

所以,在模仿实现中,咱们只有一直遍历左操作数的原型链,获得原型链上的原型对象,并与右操作数的原型对象比拟即可。

上面是具体的代码实现:

function myInstanceof(instance,constructor){if(typeof instance != 'object' && typeof instance != 'function' || instance == null){return false}
    if(typeof constructor != 'function'){throw TypeError('the right-hand-side of instanceof must be a function')
    }
    let proto = constructor.prototype
    let p = instance.__proto__
    while(p != null){if(p == proto){return true}
        p = p.__proto__
    }
}

这里还能够略微扯一下题外话。原生的 instanceof 并不反对检测根本数据类型,就和下面的实现一样,当发现左操作数是根本数据类型时,会间接返回 false。有没有方法做到让它也能检测根本数据类型呢?实际上是能够的。

依据标准的说法,在调用 instanceof 的时候,实际上会去调用外部的 @@hasInstance 办法,而这个外部办法在 ES6 中通过 [Symbol.hasInstance] 裸露进去,作为右操作数(构造函数)上的静态方法,这意味着咱们能够批改这个办法,自定义 instanceof 的返回值。

举个例子,这里要检测 1 instanceof Number,那么咱们能够通过 Object.defineProperty改写 Number[Symbol.hasInstance] 办法:

Object.defineProperty(Number,Symbol.hasInstance,{value: fucntion(x){return typeof(x)==='object'? x instanceof Number : typeof(x) === 'number'
    }                
})

当调用 1 instanceof Number的时候,理论是调用了 Number[Symbol.hasInstance](1),而且它既能够检测根本数据类型 ”number”,也能够检测它的包装对象:

1 instanceof Number                          // true
new Number(1) instanceof Number              // true
Number[Symbol.hasInstance](1)                // true
Number[Symbol.hasInstance](new Number(1))    // true  

如果不心愿批改内置类,也能够本人实现一个 MyNumber 类:

class MyNumber{static [Symbol.hasInstance](x){return typeof(x)==='object'? x instanceof Number : typeof(x) === 'number'
    }
}

成果是一样的:

1 instanceof MyNumber                          // true
new Number(1) instanceof MyNumber              // true
MyNumber[Symbol.hasInstance](1)                // true
MyNumber[Symbol.hasInstance](new Number(1))    // true  
正文完
 0