2008-12-24 91 views
16

爲了更好地理解它,我在js中做了一些繼承,並且發現了讓我困惑的東西。Javascript Prototypal Inheritance懷疑二

我知道當你用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] - > [構造函數] - > [prototype]而不是[obj] - > [prototype]是繼承鏈嗎?

3)是我們的對象(fido)的「原型」屬性嗎?如果是的話......爲什麼'deathBite'未定義(在最後一部分)?

謝謝!

回答

12

1)爲什麼在所有的新對象包含 參考生成器功能的 原型,fido.prototype是 不確定?

所有新對象都包含對構造函數在施工時出現的原型的引用。但是,用於存儲此引用的屬性名稱不是prototype,因爲它在構造函數本身上。一些Javascript實現允許通過某些屬性名稱(如__proto__)訪問此「隱藏」屬性,而其他人則不會(例如Microsofts)。

2)是對繼承鏈[OBJ] - > [構造] - > [原型] [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 prototype屬性中保存的當前引用被轉移到其對原型的對象隱藏引用。

3)是我們的 對象(fido)曾經檢查過的'原型'屬性?如果是的話...... 爲什麼'deathBite'undefined(在 的最後部分)?

分配一個對象(除功能以外)的屬性稱爲prototype沒有特殊的意義,如前面所述的對象不通過這樣的屬性名保持到它的原型參考。

+1

不錯!請注意,如果您在Derived.prototype = new Base2()之後聲明瞭var x = new Derived(),您將看到'Second'。 x只是持有指向原始Derived.prototype的指針,但這並不意味着我們實際上沒有將其重定向到Base2()。指針簡單地被改變了。一點也不矛盾,安東尼說的只是澄清最後一點。看到我的例子:http://github.com/roblevintennis/Testing-and-Debugging-JavaScript/blob/master/code/objects/lib/js_inheritance.js – Rob 2009-10-29 14:53:18

7

一旦用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構造,其中構造是function原來決心與new被調用。

+0

你可以在Mozilla(Firefox等)。但是,這通常是不切實際的。 – thomasrutter 2010-04-17 16:44:32

4

用數字回答您的問題:

  1. 對象的prototype屬性不叫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的回答中找到創建對象的順序。

2

我知道它已經被回答了,但是,有一個更好的方法來做繼承。只爲了繼承的目的調用構造函數是不可取的。其中一個不良影響是。

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

2

值得一提的是,在ECMAScript中5(即JavaScript語言的最新版本),你可以通過Object.getPrototypeOf可以訪問內部[[原型]]實例的屬性:

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