美文网首页
原型式继承中构造器被覆盖的问题

原型式继承中构造器被覆盖的问题

作者: 江枫 | 来源:发表于2014-09-19 07:47 被阅读97次

    看下面代码

    function Person(){}
    function Man(){}
    function Woman(){}
    
    Woman.prototype = new Person
    var a = new Man
    var b = new Woman
    a.constructor
    b.constructor
    

    问 a.constructor和b.constructor分别是什么?

    a.constructor
    -> function Man(){}
    b.constructor
    -> function Person(){}
    

    会不会觉得 b.constructor = function Person(){} 好像不太对劲呢?

    接下来你会干的一件事情是什么呢?既然是通过new对应的构造函数得到的对象,我猜你应该会打印下面的语句

    Man.constructor
    -> function Function() { [native code] }
    Woman.constructor
    -> function Function() { [native code] }
    Person.constructor
    -> function Function() { [native code] }
    

    可能你会反应个2-3秒,然后哈哈一笑,Man是函数,函数不都是Function构造的吗!

    现在距离真相就只差一步之遥了,接下来做什么呢?

    Man.prototype.constructor
    -> function Man(){}
    Woman.prototype.constructor
    -> function Person(){}
    Person.prototype.constructor
    -> function Person(){}
    

    原来 Woman.prototype = new Person这个操作把Woman自己的constructor属性覆盖了,为什么说是覆盖了呢?因为constructor属性在函数定义之时就已经存在了。

    这样当b访问constructor属性时在当前的Women的prototype上就找不到对应的constructor属性,进而就会向上访问对象的原型链,最终找到的是Person对象的constructor属性。

    既然知道了原因,要修正这个问题就简单了

    Woman.prototype.constructor = Woman 
    -> function Woman(){}
    var c = new Woman
    -> undefined
    c.constructor
    -> function Woman(){}
    

    new 操作符

    *ECMA262中对new操作符有下面的定义 *

    11.2.2 The new Operator

    The production NewExpression : new NewExpression is evaluated as follows:

    • Let ref be the result of evaluating NewExpression.
    • Let constructor be GetValue(ref).
    • If Type(constructor) is not Object, throw a TypeError exception.
    • If constructor does not implement the [[Construct]] internal method, throw a TypeErrorexception.
    • Return the result of calling the [[Construct]] internal method on constructor, providing no arguments (that is, an empty list of arguments).
    • The production MemberExpression : new MemberExpression Arguments is evaluated as follows:
    • Let ref be the result of evaluating MemberExpression.
    • Let constructor be GetValue(ref).
    • Let argList be the result of evaluating Arguments, producing an internal list of argument values (11.2.4).
    • If Type(constructor) is not Object, throw a TypeError exception.
    • If constructor does not implement the [[Construct]] internal method, throw a TypeErrorexception.
    • Return the result of calling the [[Construct]] internal method on constructor, providing the listargList as the argument values.

    上面的意思就是先求NewExpression的值为ref,然后在ref上调用内部的[[construct]]方法,还是比较抽象是吧,stackoverflow上有人给出了new操作符的执行流程。

    var _new = function(fn) {
      var obj = Object.create(fn.prototype);
      var result = fn.apply(obj);
      return result != null && typeof result === 'object' ? result : obj;
    };
    
    1. 看到了吧,和上面我们实验的结果是一致的。Object.create 的只是fn的原型prototype,由于原型中丢失了constructor属性,所以我们在访问的时候就会出现找到了父亲的constructor属性。

    2. 再看_new中的fn.apply(obj),new Woman的时候fn是Woman构造函数呢还是Person构造函数呢,这个我们验证一下.

      function A(){ console.log('in A'); }
      -> undefined
      function B(){ console.log('in B'); }
      -> undefined
      B.prototype = new A();
      -> in A 
      -> A {}
      b = new B()
      -> in B 
      -> B {}
      

    符合预期。

    相关文章

      网友评论

          本文标题:原型式继承中构造器被覆盖的问题

          本文链接:https://www.haomeiwen.com/subject/awkutttx.html