【问题标题】:Javascript Prototypal Inheritance?Javascript原型继承?
【发布时间】:2010-09-28 08:08:33
【问题描述】:

为了更好的理解,我在js中做了一些继承,发现了一些让我困惑的地方。

我知道,当您使用 new 关键字调用“构造函数”时,您会得到一个引用该函数原型的新对象。

我还知道,为了进行原型继承,您必须将构造函数的原型替换为您希望成为“超类”的对象的实例。

所以我做了这个愚蠢的例子来尝试这些概念:

function Animal(){}
function Dog(){}

Animal.prototype.run = function(){alert("running...")};

Dog.prototype = new Animal(); 
Dog.prototype.bark = function(){alert("arf!")};

var fido = new Dog();
fido.bark() //ok
fido.run() //ok

console.log(Dog.prototype) // its an 'Object' 
console.log(fido.prototype) // UNDEFINED
console.log(fido.constructor.prototype == Dog.prototype) //this is true

function KillerDog(){};
KillerDog.prototype.deathBite = function(){alert("AAARFFF! *bite*")}

fido.prototype = new KillerDog();

console.log(fido.prototype) // no longer UNDEFINED
fido.deathBite(); // but this doesn't work!

(这是在 Firebug 的控制台中完成的)

1) 为什么如果所有新对象都包含对创建函数原型的引用,fido.prototype 是未定义的?

2) 继承链是 [obj] -> [constructor] -> [prototype] 而不是 [obj] -> [prototype] 吗?

3) 是否检查过我们对象 (fido) 的“原型”属性?如果是这样......为什么'deathBite'未定义(在最后一部分)?

【问题讨论】:

    标签: javascript inheritance prototype prototypal-inheritance


    【解决方案1】:

    1) 为什么如果所有新对象都包含一个 对创建者函数的引用 原型,fido.prototype 是 未定义?

    所有新对象都持有对在构造时存在于其构造函数中的原型的引用。但是,用于存储此引用的属性名称不是 prototype,因为它在构造函数本身上。某些 Javascript 实现确实允许通过某些属性名称(例如 __proto__)访问此“隐藏”属性,而其他实现则不允许(例如 Microsoft)。

    2) 是继承链[obj] -> [constructor] -> [prototype] 代替 [obj] -> [原型] 的?

    没有。看看这个:-

    function Base() {}
    Base.prototype.doThis = function() { alert("First"); }
    
    function Base2() {}
    Base2.prototype.doThis = function() { alert("Second"); }
    
    function Derived() {}
    Derived.prototype = new Base()
    
    var x = new Derived()
    
    Derived.prototype = new Base2()
    
    x.doThis();
    

    这会提醒“第一”而不是第二。如果继承链通过构造函数,我们将看到“Second”。当一个对象被构造时,保存在 Functions 原型属性中的当前引用被转移到该对象对其原型的隐藏引用。

    3) 是我们的“原型”属性 对象(fido)曾经检查过吗?如果是这样的话... 为什么'deathBite'未定义(在 最后一部分)?

    将名为prototype 的属性分配给对象(函数除外)没有特殊含义,如前所述,对象不会通过此类属性名称维护对其原型的引用。

    【讨论】:

    • 不错!请注意,如果您在 Derived.prototype = new Base2() 之后声明了 var x = new Derived(),您将看到“第二个”。 x 只是持有一个指向原始 Derived.prototype 的指针,但这并不意味着我们实际上没有将它重定向到 Base2()。指针只是被改变了。与安东尼所说的完全没有矛盾,只是澄清了最后一点。看我的例子:github.com/roblevintennis/Testing-and-Debugging-JavaScript/blob/…
    • 哦,我想你的意思是:proto 不是 prototype
    • 降价,呃! underscore 下划线 proto 下划线 下划线
    • 提问者缺少的一点是原型属性仅存在于构造函数中,而不存在于对象的实例中。所以如果你有一个实例并且你想知道它的原型,你必须调用 instance.constructor.prototype。但是,如上所述,设置不会更改实例的原型,只会更改构造函数的原型..
    【解决方案2】:

    对象的原型一旦被new 实例化就无法更改。

    在你上面的例子中,像

    这样的行
    fido.prototype = new KillerDog();
    

    只需在对象fido 上创建一个名为prototype 的新属性,并将该属性设置为新的KillerDog 对象。和

    没什么区别
    fido.foo = new KillerDog();
    

    正如您的代码所代表的那样......

    // Doesn't work because objects can't be changed via their constructors
    fido.deathBite();
    
    // Does work, because objects can be changed dynamically, 
    // and Javascript won't complain when you use prototype 
    //as an object attribute name
    fido.prototype.deathBite();
    

    特殊的prototype 行为仅适用于javascript 中的构造函数,其中构造函数是functions,将使用new 调用。

    【讨论】:

    • 您可以在 Mozilla(Firefox 等)中使用。但是,这通常是不切实际的。
    【解决方案3】:

    用数字回答你的问题:

    1. 对象的原型属性不称为prototype。该标准使用[[prototype]] 来指定它。 Firefox 以 __proto__ 的名义将此属性公开。
    2. 继承链是[obj][prototype object]。您最初的假设 ([obj][constructor][prototype]) 是不正确的,您可以通过修改 constructor 和/或 constructor.prototype 轻松反驳它,并检查可以在您的 [obj] 上调用哪些方法 - 你会发现这些修改并没有改变任何东西。
    3. prototype 对象上的属性未检查且未使用。您可以将其设置为您喜欢的任何内容。 JavaScript 仅在对象构造期间在函数对象上使用它。

    为了演示#3,这里是来自Dojo的代码:

    dojo.delegate = dojo._delegate = (function(){
      // boodman/crockford delegation w/ cornford optimization
      function TMP(){}
      return function(obj, props){
        TMP.prototype = obj;
        var tmp = new TMP();
        if(props){
          dojo._mixin(tmp, props);
        }
        return tmp; // Object
      }
    })();
    

    如您所见,它利用prototype 仅在一个地方使用这一事实,通过对具有不同原型的所有委托对象重用相同的函数TMP。实际上prototype是在调用new函数之前直接赋值的,之后会改变,不会影响任何创建的对象。

    您可以在我对Relation between [[Prototype]] and prototype in JavaScript 的回答中找到对象创建顺序。

    【讨论】:

      【解决方案4】:

      我知道它已经得到了回答,但是有更好的方法来进行继承。仅出于继承目的调用构造函数是不可取的。不良影响之一是。

      function Base() {this.a = "A"}
      function Child() {this.b = "B"};
      
      Child.prototype = new Base();
      

      现在您已将属性“a”添加到您不打算添加的 Child 原型中。

      这是正确的方法(这个不是我发明的,Ext-JS 和其他库使用这个)

      // This is used to avoid calling a base class's constructor just to setup inheritance.
      function SurrogateCtor() {}
      
      /**
       * Sets a contructor to inherit from another constructor
       */
      function extend(BaseCtor, DerivedCtor) {
        // Copy the prototype to the surrogate constructor
        SurrogateCtor.prototype = BaseCtor.prototype;
        // this sets up the inheritance chain
        DerivedCtor.prototype = new SurrogateCtor();
        // Fix the constructor property, otherwise it would point to the BaseCtor
        DerivedCtor.prototype.constructor = DerivedCtor;
        // Might as well add a property to the constructor to 
        // allow for simpler calling of base class's method
        DerivedCtor.superclass = BaseCtor;
      }
      
      function Base() {
        this.a = "A";
      }
      
      Base.prototype.getA = function() {return this.a}
      
      function Derived() {
        Derived.superclass.call(this);  // No need to reference the base class by name
        this.b = "B";
      }
      
      extend(Base, Derived);
      // Have to set methods on the prototype after the call to extend
      // otherwise the prototype is overridden;
      Derived.prototype.getB = function(){return this.b};
      var obj = new Derived();
      

      一个更简单的方法是在你指定派生类的方法的地方添加第三个参数来扩展,这样你就不必调用扩展然后将方法添加到原型中

      extend(BaseCtor, DerivedCtor, {
        getB: function() {return this.b}
      });
      

      那么你可以为语法糖做很多其他的事情。

      博客:http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

      【讨论】:

        【解决方案5】:

        值得注意的是,在 ECMAScript 5(即最新版本的 JavaScript 语言)中,您可以通过 Object.getPrototypeOf 访问实例的内部 [[Prototype]] 属性:

        Object.getPrototypeOf(fido) === Dog.prototype
        

        【讨论】:

          猜你喜欢
          • 2018-02-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多