JS魔法堂:再识instanceof

页面导航:首页 > 网络编程 > JavaScript > JS魔法堂:再识instanceof

JS魔法堂:再识instanceof

来源: 作者: 时间:2016-01-19 18:32 【

一、Breif 大家都知道instanceof一般就是用来检查A对象是否为B类或子类的实例。那问题是JS中没有类的概念更没有类继承的概念(虽然有构造函数),那
一、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()
// 假如Script Engine实现了Objects Joined,那么
c === d 返回值为true。因为a中定义b函数啥都一样,所以底层实现可以不再生成一个新的Function object,从而从空间和时间上降低消耗。

 

 
Tags:

文章评论

最 近 更 新
热 点 排 行
Js与CSS工具
代码转换工具

<