Javascript Prototypal Inheritance?
为了更好地理解JS,我在JS中做了一些继承,发现了一些让我困惑的东西。
我知道,当用new关键字调用"constructor函数"时,会得到一个引用该函数原型的新对象。
我还知道,为了进行原型继承,必须用要成为"超类"的对象的实例替换构造函数函数的原型。
所以我做了这个愚蠢的例子来尝试这些概念:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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)为什么所有新对象都包含对creator函数原型的引用,fido.prototype未定义?
2)继承链是[obj]->[constructor]->[prototype]而不是[obj]->[prototype]?
3)是否检查过我们的对象(FIDO)的"原型"属性?如果是这样…为什么(最后一部分)没有定义"死亡之咬"?
1) Why if all new objects contain a
reference to the creator function's
prototype, fido.prototype is
undefined?
所有新对象都具有对构建时其构造函数上存在的原型的引用。但是,用于存储此引用的属性名不是
2) Is the inheritance chain [obj] ->
[constructor] -> [prototype] instead
of [obj] -> [prototype] ?
不,看看这个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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(); |
这会提醒"第一个"而不是第二个。如果继承链通过构造函数,我们将看到"第二个"。构造对象时,函数原型属性中的当前引用将传输到对象对其原型的隐藏引用。
3) is the 'prototype' property of our
object (fido) ever checked? if so...
why is 'deathBite' undefined (in the
last part)?
如前所述,为对象(函数除外)分配名为
一旦用
在上面的示例中,行如下
1 | fido.prototype = new KillerDog(); |
只需在对象
1 | fido.foo = new KillerDog(); |
正如你的代码所示…
1 2 3 4 5 6 7 | // 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(); |
特殊的
用数字回答你的问题:
为了演示3,这里是Dojo的代码:
4正如您所看到的,它利用了这样一个事实:通过对具有不同原型的所有委托对象重复使用相同的函数
你可以在我的答案中找到对象创建序列,它与JavaScript中的原型之间的关系。
值得注意的是,在ecmascript 5(即最新版本的javascript语言)中,您可以通过
1 | Object.getPrototypeOf(fido) === Dog.prototype |
我知道已经有人回答过了,但有更好的方法来继承遗产。仅仅为了继承而调用构造函数是不可取的。其中一个不受欢迎的影响是。
1 2 3 4 | function Base() {this.a ="A"} function Child() {this.b ="B"}; Child.prototype = new Base(); |
现在,您已经将属性"A"添加到了您不打算添加的儿童原型中。
这是正确的方法(我没有发明这个,ExtJS和其他libs使用这个)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | // 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,然后将方法添加到原型中。
1 2 3 | extend(BaseCtor, DerivedCtor, { getB: function() {return this.b} }); |
还有很多其他的事情你可以做的句法糖。
博客:http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html