一、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比較實在。
尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/4231454.html 肥仔John^_^
六、Thanks
http://www.w3cfuns.com/article-5597466-1-1.html
http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/
文章列表