引子
首先,在我们手撕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