引子
首先,在我们手撕instanceof的时候,我们先来看看他都有什么用处?
let arr = [] let fn = function fn() {} let obj = {} let num = 2020 console.log(arr instanceof Array) //true console.log(arr instanceof Object) //true console.log(arr instanceof Function) //false console.log(arr instanceof Number) //false console.log(fn instanceof Array) //false console.log(fn instanceof Object) //true console.log(fn instanceof Function) //true console.log(fn instanceof Number) //false console.log(obj instanceof Array) //false console.log(obj instanceof Object) //true console.log(obj instanceof Function) //false console.log(obj instanceof Number) //false console.log(num instanceof Array) //false console.log(num instanceof Object) //false console.log(num instanceof Function) //fasle console.log(num instanceof Number) //fasle
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。 —MDN
上面这段话来自MDN,可以说是很形象了,检测构造函数的原型是否出现在某个实例的原型链上,
i.e. & e.g.
instanceof 运算符用于检测Array.prototype属性是否出现在 arr._proto.__proto…上
正文
instanceof的原理
- 内置类的Symbol.hasInstance属性方法
- 检测构造函数的prototype是否出现在实例的__proto__上
- 不能检测基本数据类型
function instance_of(example, classFunc) { // 检测classFunc必须是个函数 if( typeof classFunc !== function ) throw new TypeError('') // 先看看浏览器支持不支持Symbol,此处延伸:Symbol的作用?Symbol兼容哪些浏览器? if( typeof Symbol !== 'undefined' ) { //支持的话先看看classFunc有没有 Symbol.hasInstance这个属性,一般情况下,内置类都会有,可以直接用来检测一个实例,并且他是一个函数,接受一个参数 var hasInstance = classFunc[Symbol.hasInstance] if( typeof hasInstance === 'function' ) { //此处用call改变一下this指向,延伸:为甚么此处hasInstance不能直接调用?this指向了谁? return hasInstance.call(classFunc, example) } } var proto = example.__proto__, prototype = classFunc.prototype if( proto && prototype) { // 实例必须有__proto__ && 函数必须有 prototype // 基本数据类型没有 __proto__ 箭头函数没有 prototype, class类里的fn(){}这种函数也没有 prototype while(true){ if( proto === null ) return false //找到头了 if( proto === prototype ) return true // 找到了 proto = proto.__proto__ } } else { return false } } instance_of([], Array) // true