一、Breif %26nbsp;%26nbsp;
%26nbsp; 大家都知道instanceof一般就是用来检查A对象是否为B类或子类的实例。那问题是JS中没有类的概念更没有类继承的概念(虽然有构造函数),那么instanceof到底是怎样判断A对象是B构造函数的实例呢?本文将对此作分析记录,以便日后查阅。
%26nbsp;
二、Reference 2 ECMA-262-3 Spec%26nbsp; %26nbsp;
%26nbsp;http://bclary.com/2004/11/07/#a-11.8.6
The production%26nbsp;RelationalExpression: RelationalExpression%26nbsp;instanceof%26nbsp;ShiftExpression%26nbsp;is evaluated as follows:
1. Evaluate%26nbsp;RelationalExpression.
2.Call%26nbsp;GetValue(Result(1)).
3.Evaluate%26nbsp;ShiftExpression.
4.Call%26nbsp;GetValue(Result(3)).
5.If Result(4) is not an object, throw a%26nbsp;TypeError%26nbsp;exception.
6.If Result(4) does not have a%26nbsp;[[HasInstance]]%26nbsp;method, throw a%26nbsp;TypeError%26nbsp;exception.
7.Call the%26nbsp;[[HasInstance]]%26nbsp;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]]的定义
%26nbsp;http://bclary.com/2004/11/07/#a-15.3.5.3
Assume%26nbsp;F%26nbsp;is a Function object.
When the%26nbsp;[[HasInstance]]%26nbsp;method of%26nbsp;F%26nbsp;is called with value%26nbsp;V, the following steps are taken:
1. If%26nbsp;V%26nbsp;is not an object, return%26nbsp;false.
2. Call the%26nbsp;[[Get]]%26nbsp;method of%26nbsp;F%26nbsp;with property name%26nbsp;"prototype".
3. Let%26nbsp;O%26nbsp;be Result(2).
4. If%26nbsp;O%26nbsp;is not an object, throw a%26nbsp;TypeError%26nbsp;exception.
5. Let%26nbsp;V%26nbsp;be the value of the%26nbsp;[[Prototype]]%26nbsp;property of%26nbsp;V.
6. If%26nbsp;V%26nbsp;is%26nbsp;null,%26nbsp;return%26nbsp;false.
7. If%26nbsp;O%26nbsp;and%26nbsp;V%26nbsp;refer to the same object or if they refer to objects joined to each other (13.1.2), return%26nbsp;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是有限链表);
也许大家会对%26nbsp;Function.prototype['[[HasInstance]]']%26nbsp;的实现为什么能成功感到疑问,我们先看看以下图片
可以知道所有函数的%26nbsp;__proto__%26nbsp;默认情况下均指向%26nbsp;Function.prototype%26nbsp;,而%26nbsp;Function.__proto__%26nbsp;则与%26nbsp;Function.prototype%26nbsp;指向同一个对象。
Chrome中两者均指向%26nbsp;function Empty(){}%26nbsp;,因此添加到Function.protoype的属性,也会出现在Function的prototype chain中。
%26nbsp;
四、About%26nbsp;if they refer to objects joined to each other%26nbsp;
%26nbsp; 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,从而从空间和时间上降低消耗。
%26nbsp;
五 、Conclusion
%26nbsp; 之前看了很多讲述instanceof的文章但始终对它理解得不透彻,看来还是看Spec比较实在。
%26nbsp; 尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/4231454.html%26nbsp;肥仔John^_^
%26nbsp;
六、Thanks
http://www.w3cfuns.com/article-5597466-1-1.html
http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/
%26nbsp;