JS魔法堂:再识instanceof

一、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]]'] 的实现为什么能成功感到疑问,我们先看看以下图片

可以知道所有函数的 __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/

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏向治洪

Java 8新特性

编者注:Java 8已经公布有一段时间了,种种迹象表明Java 8是一个有重大改变的发行版。 在Java Code Geeks上已经有大量的关于Java 8 的...

3686
来自专栏阿杜的世界

【译】Java 8的新特性—终极版1. 简介2. Java语言的新特性3. Java编译器的新特性4. Java官方库的新特性5. 新的Java工具6. JVM的新特性7. 结论8. 参考资料

前言: Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级。在Java Code Geeks上已经有很多介绍Java 8新特性的文章,...

1124
来自专栏积累沉淀

【译】Java 8的新特性—终极版

声明:本文翻译自Java 8 Features Tutorial – The ULTIMATE Guide,翻译过程中发现并发编程网已经有同学翻译过了:...

24410
来自专栏屈定‘s Blog

Java8 Lambda(三)-强大的collect操作

collect应该说是Stream中最强大的终端操作了,使用其几乎能得到你想要的任意数据的聚合,下面好好分析该工具的用法.

9922
来自专栏蘑菇先生的技术笔记

探索c#之不可变数据类型

2064
来自专栏林德熙的博客

WPF 判断调用方法堆栈

最近遇到一个问题,经常有小伙伴在类A的构造里调用静态函数B,但是这时B依赖于A的初始化完成,于是就无限循环。所以我需要在判断小伙伴调用B时是否在A的构造方法里,...

691
来自专栏小樱的经验随笔

【Java学习笔记之二十八】深入了解Java8新特性

前言: Java 8 已经发布很久了,很多报道表明java 8 是一次重大的版本升级。在Java Code Geeks上已经有很多介绍Java 8新特性的文章,...

3587
来自专栏守望轩

c#的细节(一)-问号的细节

写在最前面的话: 《c#的细节》是我当初学习c#时候,做的一些学习笔记的整理,现在回头看来这些都是非常浅显的知识,是c#非常细节的地方,同时也是不能忽略的地方,...

2036
来自专栏HansBug's Lab

1901: Zju2112 Dynamic Rankings

1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MB Submit: ...

2776
来自专栏函数式编程语言及工具

Scalaz(53)- scalaz-stream: 程序运算器-application scenario

    从上面多篇的讨论中我们了解到scalaz-stream代表一串连续无穷的数据或者程序。对这个数据流的处理过程就是一个状态机器(state machine...

1989

扫码关注云+社区

领取腾讯云代金券