资讯专栏INFORMATION COLUMN

JS魔法堂:再识instanceof

gself / 1178人阅读

摘要:一大家都知道一般就是用来检查对象是否为类或子类的实例。中两者均指向,因此添加到的属性,也会出现在的中。四其实是建议实现者如采用的底层优化手段。因为中定义函数啥都一样,所以底层实现可以不再生成一个新的,从而从空间和时间上降低消耗。

一、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:

Evaluate RelationalExpression.

Call GetValue(Result(1)).

Evaluate ShiftExpression.

Call GetValue(Result(3)).

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

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

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

Return Result(7).

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

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

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底层的运算机制关键点如下:

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

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

当且仅当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/

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85848.html

相关文章

  • JS魔法:那些困扰你的DOM集合类型

    摘要:五的子类对象会返回一个集合对象,集合内存储类型的元素。七的子类初看很有可能以为集合元素就是单选表单元素,其实可以存储任意类型的表单元素。八的子类开始,将返回子类的对象,其行为特征和一致。但在前,我们应该先了解清楚的类型的特征。 一、前言                            大家先看看下面的js,猜猜结果会怎样吧! 可选答案: ①. 获取id属性值为id的节点元素 ②...

    468122151 评论0 收藏0
  • 前端魔法——异常不仅仅是try/catch

    摘要:我打算分成前端魔法堂异常不仅仅是和前端魔法堂调用栈,异常实例中的宝藏两篇分别叙述内置自定义异常类,捕获运行时异常语法异常网络请求异常事件,什么是调用栈和如何获取调用栈的相关信息。 前言  编程时我们往往拿到的是业务流程正确的业务说明文档或规范,但实际开发中却布满荆棘和例外情况,而这些例外中包含业务用例的例外,也包含技术上的例外。对于业务用例的例外我们别无它法,必须要求实施人员与用户共同...

    bladefury 评论0 收藏0
  • JS魔法:定义页面的Dispose方法——[before]unload事件启示录

    摘要:坑无视和是十分特殊的事件,要求事件处理函数内部不能阻塞当前线程,而却恰恰就会阻塞当前线程,因此规范中以明确在和中直接无视这几个方法的调用。 前言  最近实施的同事报障,说用户审批流程后直接关闭浏览器,操作十余次后系统就报用户会话数超过上限,咨询4A同事后得知登陆后需要显式调用登出API才能清理4A端,否则必然会超出会话上限。 即使在页面上增添一个登出按钮也无法保证用户不会直接关掉浏览器...

    Chiclaim 评论0 收藏0
  • CSS魔法:重拾Border之——更广阔的遐想

    摘要:也就是说我们操作的几何公式中的未知变量,而具体的画图操作则由渲染引擎处理,而不是我们苦苦哀求设计师帮忙。 前言  当CSS3推出border-radius属性时我们是那么欣喜若狂啊,一想到终于不用再添加额外元素来模拟圆角了,但发现border-radius还分水平半径和垂直半径,然后又发现border-top-left/right-radius的水平半径之和大于元素宽度时,实际值会按比...

    lily_wang 评论0 收藏0
  • CSS魔法:重拾Border之——解构Border

    摘要:本系列将稍微深入探讨一下那个貌似没什么好玩的魔法堂重拾之解构魔法堂重拾之图片作边框魔法堂重拾之不仅仅是圆角魔法堂重拾之更广阔的遐想解构说起我们自然会想起,而由条紧紧包裹着的边组成,所以的最小操作单元是。 前言  当CSS3推出border-radius属性时我们是那么欣喜若狂啊,一想到终于不用再添加额外元素来模拟圆角了,但发现border-radius还分水平半径和垂直半径,然后又发现...

    lingdududu 评论0 收藏0

发表评论

0条评论

gself

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<