http://www.cnblogs.com/jasonxuli/p/6769282.html
這是 2014-12-10 發在 iteye 上的文章
今天突然想起js的原型繼承模型和相關的prototype,constructor,覺得有點模糊,就寫了個例子:
var log = console.log; function A(){ this.value = 1; } var a = new A(); log('a.value = ', a.value); // a.value = 1 log('a instanceof A: ', a instanceof A); // a instanceof A: true function B(){ this.value = 2; } var b = new B(); A.prototype = b; var aa = new A(); log(aa.constructor == b.constructor); // true log('a.value = ', a.value); // a.value = 1 log('b.value = ', b.value); // b.value = 2 log('aa.value = ', aa.value); // aa.value = 1 log('a instanceof A: ', a instanceof A); // a instanceof A: false log('a instanceof B: ', a instanceof B); // a instanceof B: false
其他的都沒問題,最后兩行突然有點讓我恍惚:
為什么在A繼承了B之后,a就不是A的實例了呢?
於是就去查instanceof的文檔,ECMA 262 5.1版:
11.8.6The instanceof operator
The production RelationalExpression : RelationalExpression instanceof
ShiftExpression is evaluated as follows:
- Let lref be the result of evaluating RelationalExpression.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating ShiftExpression.
- Let rval be GetValue(rref).
- If Type(rval) is not Object, throw a TypeError exception.
- If rval does not have a [[HasInstance]] internal method, throw a TypeError exception.
- Return the result of calling the [[HasInstance]] internal method of rval with argument lval.
2, 4 條提到的GetValue又是一個坑,暫且不管,去看6, 7提到的內部方法[[HasInstance]]:
15.3.5.3[[HasInstance]] (V)
Assume F is a Function object.
When the [[HasInstance]] internal method of F is called with value V, the following steps are taken:
- If V is not an object, return false.
- Let O be the result of calling the [[Get]] internal method of F with property name
"prototype"
. - If Type(O) is not Object, throw a TypeError exception.
- Repeat
- Let V be the value of the [[Prototype]] internal property of V.
- If V is
null
, return false. - If O and V refer to the same object, return true.
這一條大概等同於下面的邏輯:
F為instanceof表達式的右值,也就是constructor;V是左值,也就是instance;
HasInstance(V){ if(typeof(V) != 'object') return false; var O = F.get('prototype'); // get 為內部方法 if(typeof(O) != Object) throw new TypeError(); while(1){ // 循環實例V的原型鏈 V = V.__proto__; // 獲取實例V的內部屬性prototype,可以用Object.getPrototypeOf(V)獲取 if(V == null) // Object.prototype.__proto__ 為null,這也是最終的跳出語句 return false; if(O === V) return true; } } F.HasInstance(V);
查了文檔,算是搞明白了:
比較時使用的是實例的內部屬性__proto__和構造函數的prototype,而實例內部屬性__proto__只在被初始化的時候被設置為構造函數的prototype,因此
A.prototype = b; // A 繼承了 B
而a的內部的__proto__還是A{},也就是a記得自己的親爹是A{},但是現在的A認B做父了,所以a不承認是A的直系了。
a instanceof A; // false
a instanceof B; // false
有點兒刻舟求劍的感覺不?!