在 JavaScript 中,咱们一般用 typeof
判断类型,可是在判断引用类型的值时,经常会遇到一个问题:不管引用的是什么类型的对象,都会返回 "object"(固然还有 "function") 。有时候咱们须要知道这个引用对象的类型是数组仍是一个包装对象,这个时候 instanceof
就能够派上用场了。git
废话很少说,先来几个例子热身一下,所有都知道同窗,请点击右上角的关闭按钮;模模糊糊的同窗,能够继续阅读,只要掌握了原理,这些题目真的是易如反掌。github
const a = 'abc'; console.log(a instanceof String); // ? const b = new String('abc'); console.log(b instanceof String); // ?
console.log(String instanceof String); // ? console.log(Object instanceof Object); // ? console.log(Function instanceof Function); // ? console.log(Function instanceof Object); // ?
在 MDN 上是这样描述 instanceof
的:面试
instanceof
运算符用于测试构造函数的prototype
属性是否出如今对象原型链中的任何位置
换句话说,若是A instanceof B
,那么 A
必须是一个对象,而 B
必须是一个合法的 JavaScript 函数。在这两个条件都知足的状况下:数组
判断 B 的 prototype 属性指向的原型对象(B.prototype)是否在对象 A 的原型链上。若是在,则为 true;若是不在,则为 false。函数
下面咱们举一个例子一步步来讲明:学习
function Person() {} const p1 = new Person(); p1 instanceof Person; // true
第一步:每个构造函数都有一个 prototype
属性。测试
第二步:这个 prototype
属性指向这个构造函数的原型对象spa
第三步:经过 new
关键字,能够建立一个构造函数的实例(这里是 p1),而实例上都有一个 __proto__
属性prototype
第四步:实例上的 __proto__
属性也指向构造函数的原型对象,这样咱们就能够获得一张完整的关系图了code
第五步:p1 instanceof Person
,检查 B(Person) 的 prototype
属性指向的原型对象,是否在对象 A(p1) 的原型链上。
通过咱们的一步步分解,发现 B(Person) 的 prototype
所指向的原型对象确实在 A(p1) 的原型链上,因此咱们能够肯定 p1 instanceof Person
必定是为 true
的。
咱们再深刻一点会发现,不只仅 p1 instanceof Person
为 true
,p1 instanceof Object
也为 true
,这又是为何呢?
其实,Person
的原型对象上也有一个 __proto__
属性,而这个属性指向 Object
的 prototype
属性所指向的原型对象,咱们能够在控制台打印一下:
既然有这个关系,那咱们再完善一下上面的图:
经过 Person
的例子,咱们知道构造函数 Object
上的 prototype
属性会指向它的原型对象:
如今,咱们要判断 p1 instanceof Object
的真假,还记得上面的定义么?咱们再来一遍:
判断 B 的 prototype 属性指向的原型对象(B.prototype)是否在对象 A 的原型链上。若是在,则为 true;若是不在,则为 false。
此时,咱们发现 B(Object) 的 prototype
属性所指向的原型对象依然在 A(p1) 的原型链上,因此结果为 true
。
经过上面的例子咱们能够知道,其实 instanceof
的原理很是简单,就是一个查找原型链的过程,因此只要你理解了原型链的相关知识,理解 instanceof
的原理就不会再有问题了。这里咱们稍微总结两点与instanceof
有关的原型链知识:
__proto__
属性,只有 Object.prototype.__proto__ === null
;prototype
属性指向它的原型对象,而构造函数实例的 __proto__
属性也指向该原型对象;看了上面的过程,其实也很容易给出 instanceof
的实现方式:
function instance_of(left, right) { const RP = right.prototype; // 构造函数的原型 while(true) { if (left === null) { return false; } if (left === RP) { // 必定要严格比较 return true; } left = left.__proto__; // 沿着原型链从新赋值 } }
有了上面的实现方法,咱们再解释一下上面的例子:
function Person() {} const p1 = new Person(); p1 instanceof Object; // 用上面的代码解释它
第一次赋值
left = p1 right = Object RP = Object.prototype
第一次判断
left !== null
而且 left !== RP
,继续向上寻找 left
的原型链,准备新的赋值。
第二次赋值
left = p1.__proto__ = Person.prototype
第二次判断
left !== null
而且 left !== RP
,继续向上寻找 left
的原型链,准备新的赋值。
第三次赋值
left = p1.__proto__.__proto__ = Person.prototype.__proto__
第三次赋值
left !== null
,此时 left === RP
,返回 true
,函数执行完毕。
今天,咱们用一个例子,经过画图以及代码实现两个角度剖析了 instanceof
的实现原理,其实思路也很简单,无非就是一个沿原型链向上查找的过程。但愿你们能够在之后的面试过程当中,再也不被" instanceof
的实现原理是什么?"这样的面试难住了。
固然,阅读永远都只是一种十分被动的学习方法,我仍是建议你能本身实践一下。在文章的开头有几个例子,感兴趣的同窗能够挑选一个例子,本身经过画图以及代码实现两种方式再加深一遍理解,相信你会理解的更深入。
若是文章中错误或表述不严谨的地方,欢迎指正。
最后,文章会首先发布在个人 Github ,以及公众号上,欢迎关注。