今天突然想起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
有点儿刻舟求剑的感觉不?!