0
点赞
收藏
分享

微信扫一扫

JS魔法堂:再识instanceof

无聊到学习 2022-03-24 阅读 39


一、Breif                               

  大家都知道instanceof一般就是用来检查A对象是否为B类或子类的实例。那问题是JS中没有类的概念更没有类继承的概念(虽然有构造函数),那么instanceof到底是怎样判断A对象是B构造函数的实例呢?本文将对此作分析记录,以便日后查阅。


二、Reference 2 ECMA-262-3 Spec                

 http://bclary.com/2004/11/07/#a-11.8.6


The production ​RelationalExpression: RelationalExpression​ ​instanceof​ ​ShiftExpression​ is evaluated as follows:

1. Evaluate ​RelationalExpression​.

2.Call ​​GetValue​​(Result(1)).

3.Evaluate ​ShiftExpression​.

4.Call ​​GetValue​​(Result(3)).

5.If Result(4) is not an object, throw a ​TypeError​ exception.

6.If Result(4) does not have a ​​[[HasInstance]]​​ method, throw a ​TypeError​ exception.

7.Call the ​​[[HasInstance]]​​ method of Result(4) with parameter Result(2).

8.Return Result(7).


从上述的定义我们可以得出以下内容:

1. ShiftExpression的实际值(GetValue(Evaluate(ShiftExpression)))必须为[object Function],否则就抛TypeError异常;

2. instanceof的实际判断则是调用​RelationalExpression​的Internal Method [[HasInstance]]来处理。

下面我们深入一下[[HasInstance]]的定义

 http://bclary.com/2004/11/07/#a-15.3.5.3


Assume ​F​ is a Function object.

When the ​​[[HasInstance]]​​ method of ​F​ is called with value ​V​, the following steps are taken:

1. If ​V​ is not an object, return ​false.

2. Call the ​​[[Get]]​​ method of ​F​ with property name ​"prototype".

3. Let ​O​ be Result(2).

4. If ​O​ is not an object, throw a ​TypeError​ exception.

5. Let ​V​ be the value of the ​​[[Prototype]]​​ property of ​V​.

6. If ​V​ is ​null,​ return ​false​.

7. If ​O​ and ​V​ refer to the same object or if they refer to objects joined to each other (​​13.1.2​​), return ​true.

8. Go to step 5.


上面的定义看得不太明白,我们把它翻译成JS的实现吧

// IE5.5~9下,由于无法通过__proto__访问对象的Internal Property [[Prototype]],因此该方法无效
;(function(rNotObj){
Function.prototype['[[HasInstance]]'] = function(value){
// 1. If V is not an object, return false
if (rNotObj.test(typeof value)) return false
// 2. Call the [[Get]] method of F with property name "prototype"
// 4. If O is not an object, throw a TypeError exception
var O = this.prototype
if (rNotObj.test(typeof O)) throw TypeError()

// 5. Let V be the value of the [[Prototype]] prototype of V
// 6. If V is null, return false
if (null === (value = value.__proto__)) return false

// 7. If O and V refer to the same object
// 8. Go to step 5
return O === value || this['[[HasInstance]]'](value)
}
}(/$[^of]/ /*not begin with o(bject) neither f(unction)*/))


现在稍微总结一下,a instanceof b底层的运算机制关键点如下:

1. b的数据类型必须为[object Function],否则就抛TypeError;

2. 若a为Primitive Value则直接返回false, 若a的数据类型为Object则执行后续运算;

3. 当且仅当b.prototype位于a的prototype chain中时,才返回true​(由于Object.prototype.__proto__为null,因此prototype chain是有限链表);

也许大家会对 Function.prototype['[[HasInstance]]'] 的实现为什么能成功感到疑问,我们先看看以下图片

JS魔法堂:再识instanceof_javascript

可以知道所有函数的 __proto__ 默认情况下均指向 Function.prototype ,而 Function.__proto__ 则与 Function.prototype 指向同一个对象。

Chrome中两者均指向 function Empty(){} ,因此添加到Function.protoype的属性,也会出现在Function的prototype chain中。


四、About ​if they refer to objects joined to each other 

  Objects Joined其实是Spec建议实现者(如V8、SpiderMonkey)采用的底层优化手段。

function a(){
function b(){}
return b
}
var c = a()
var d = a()
// 假如JavaScript Engine实现了Objects Joined,那么
c === d 返回值为true。因为a中定义b函数啥都一样,所以底层实现可以不再生成一个新的Function object,从而从空间和时间上降低消耗。



五 、Conclusion                        

  之前看了很多讲述instanceof的文章但始终对它理解得不透彻,看来还是看Spec比较实在。


六、Thanks                          

http://www.w3cfuns.com/article-5597466-1-1.html

http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/

欢迎添加我的公众号一起深入探讨技术手艺人的那些事!

JS魔法堂:再识instanceof_javascript_02


  



举报

相关推荐

0 条评论