JS的 instanceof 方法

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:

  1. Let lref be the result of evaluating RelationalExpression.
  2. Let lval be GetValue(lref).
  3. Let rref be the result of evaluating ShiftExpression.
  4. Let rval be GetValue(rref).
  5. If Type(rval) is not Object, throw a TypeError exception.
  6. If rval does not have a [[HasInstance]] internal method, throw a TypeError exception.
  7. 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:

  1. If V is not an object, return false.
  2. Let O be the result of calling the [[Get]] internal method of F with property name "prototype".
  3. If Type(O) is not Object, throw a TypeError exception.
  4. Repeat
    1. Let V be the value of the [[Prototype]] internal property of V.
    2. If V is null, return false.
    3. 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

 

有点儿刻舟求剑的感觉不?!

posted @ 2017-04-26 16:30  牛太黑  阅读(2170)  评论(0编辑  收藏  举报