努力让学习成为一种习惯,自信来源于充分的准备
如果你觉得该文章对你有帮助,欢迎大家点赞、关注和分享
instanceof
这里直接引用mdn的描述
instanceof运算符用来检测constructor.prototype是否存在于参数object的原型链上
语法:object instanceof constructor
我们直接看例子:
const obj = {}
console.log(obj instanceof Object); // true
const arr = []
console.log(arr instanceof Array); // true
function Foo() {}
const f = new Foo()
console.log(f instanceof Foo); // true
console.log(f instanceof Object); // true
相信大家对instanceof概念以及使用有了基本的认知
在具体实现之前,我们结合规范深挖下instanceof的细节
我们简单翻译一下:
- instanceof运算符右边的变量(
target)必须是对象类型,否则抛出异常
2、3. 尝试获取target的内部方法@@hasInstance(本身和继承的),如果有,则调用
这里部分小伙伴可能对@@hasInstance不太了解,这里简单介绍下
当我们执行 obj instaceof Foo的时候,实际上执行的是Foo函数内部的一个静态方法Foo[Symbol.hasInstance](obj),如果函数本身没有定义的话,则会调用Function.prototype[Symbol.hasInstance](obj)(Symbol.hasInstance与@@hasInstance是一回事)
Function实例的[@@hasInstance]()方法指定确定构造函数是否将对象识别为其实例的默认过程。它由instanceof运算符调用
换句话说:我们可以自定义instanceof的行为。如果没有定义,则采用Function.prototype上定义的默认行为,其中Function.prototype [ @@hasInstance ] (V)调用的是OrdinaryHasInstance
Function.prototype[@@hasInstance]() 属性是不可配置且不可写的。这是一个安全特性,用于防止绑定函数的底层目标函数被获取
我们直接来看代码加深下理解
// 默认情况
class Tc {}
const tc = new Tc()
console.log(Tc[Symbol.hasInstance](tc) === tc instanceof Tc); // true
// 自定义情况
class MyArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
console.log([] instanceof MyArray); // true
console.log(Function.prototype[Symbol.hasInstance].call(MyArray, [])); // false
4、5.如果没有实现@@hasInstance,而调用OrdinaryHasInstance
这里4、5 两步主要是为了兼容ECMA早期的版本(没有通过@@instance属性去定义instanceof语法语义),结合上面第3步讲到的,我们可以知道OrdinaryHasInstance内部逻辑和目前instanceof默认逻辑即Function.prototype[Symbol.hasInstance]()的判定逻辑是一样的
到这里相信大家对instanceof有了基础的认识了。接下来我们来看看OrdinaryHasInstance内部做了哪些事情,并根据它来实现自己的instanceof
实现
我们针对整体一些针对参数限制的做处理也就是 1、3两步
O instanceof C
C如果不是可调用对象即函数,直接返回 false(如果不是对象即基本类型 则抛出TypeError异常,这是前面规范第一步的要求)
O如果不是对象(也就是基本类型),直接返回 false
const isBasicType = (val = '') => {
const t = Object.prototype.toString.call(val)
if (t == '[object String]' ||
t == '[object Boolean]' ||
t == '[object Number]' ||
t == '[object Null]' ||
t == '[object Undefined]' ||
t == '[object Symbol]') {
return true;
}
return false
}
function myInstanceof(C, O) {
if(isBasicType(C)) {
throw new TypeError(`Right-hand side of 'instanceof' is not an object`)
}
if (typeof C !== 'function' || (isBasicType(O))) {
return false
}
}
接下来我们看最为核心的 4-6步 实现
function myInstanceof(C, O) {
const p = C.prototype
if(isBasicType(p)) {
throw new TypeError(`function prototype is not a object`)
}
let oProto = Object.getPrototypeOf(O)
while(oProto) {
if (oProto === null) return false
if (oProto === p) {
return true
}
oProto = Object.getPrototypeOf(oProto)
}
return false
}
好了我们看下整体代码
const isBasicType = (val = '') => {
const t = Object.prototype.toString.call(val)
if (t == '[object String]' ||
t == '[object Boolean]' ||
t == '[object Number]' ||
t == '[object Null]' ||
t == '[object Undefined]' ||
t == '[object Symbol]') {
return true;
}
return false
}
function myInstanceof(C, O) {
if(isBasicType(C)) {
throw new TypeError(`Right-hand side of 'instanceof' is not an object`)
}
if (typeof C !== 'function' || (isBasicType(O))) {
return false
}
const p = C.prototype
if(isBasicType(p)) {
throw new TypeError(`function prototype is not a object`)
}
let oProto = Object.getPrototypeOf(O)
while(oProto) {
if (oProto === null) return false
if (oProto === p) {
return true
}
oProto = Object.getPrototypeOf(oProto)
}
return false
}
我们验证下
console.log(myInstanceof(null, {})); // TypeError
console.log(myInstanceof({}, {})); // false
console.log(myInstanceof(Object, 1)); // false
console.log(myInstanceof(Object, {})); // true
console.log(myInstanceof(Array, [])); // true
console.log(myInstanceof(Object, [])); // true
console.log(myInstanceof(Function, [])); // false
function Func() {}
const f = new Func()
console.log(myInstanceof(Func, f)); // true
console.log(myInstanceof(Object, Func)); // true
function Func2() {}
Func2.prototype = f
const f2 = new Func2()
console.log(myInstanceof(Func2, f2)); // true
console.log(myInstanceof(Func, f2)); // true
console.log(myInstanceof(Object, Function)); // true
console.log(myInstanceof(Object, Array)); // true
console.log(myInstanceof(Function, Array)); // true
没问题,完美😤。但是就。。。完了吗?
还有一点我们需要关注下就是传进来的函数是bind函数的时候
由规范可知,当函数是bind函数时,会去获取其内部属性[[BoundTargetFunction]]
function func() {}
const funcBind = func.bind()
const f1 = new func()
const fBind = new funcBind()
console.log(f1 instanceof func); // true
console.log(f1 instanceof funcBind); // true
console.log(fBind instanceof func); // true
console.log(fBind instanceof funcBind); // true
我查了资料没找到获取内部属性[[BoundTargetFunction]]的方法,故上面的测例这份代码无法通过,不过最核心的原理我们已经搞定。也明白了针对bind。js引擎内部是怎么处理的😄
最后
到这里,就是本篇文章的全部内容了
如果你觉得该文章对你有帮助,欢迎大家点赞、关注和分享
如果你有疑问或者出入,评论区告诉我,我们一起讨论