2011-05-13 53 views
4

我不明白爲什麼每個人都使用Boy.prototype = new Human;來模擬繼承。看,我們想要的是A的功能嗎?我們可以做到這一點,而沒有實例化一個新的A(實際上實例化一個新的A會給我們帶來不希望的結果,因爲我們實際上正在運行實例化函數,這不是我們想要的)爲什麼我們要用`Boy.prototype = new Human;`來模擬繼承?

所以不是這個更好的解決方案?

for (var prop_name in Human.prototype) { 
Object.defineProperty(
      Boy.prototype, 
      prop_name, 
      Object.getOwnPropertyDescriptor(Human.prototype,prop_name) 
     ); 
} 

說我們是那個特定的,並希望不僅是枚舉的屬性在Human.prototype我們仍然可以通過使用Object.getOwnPropertyNames並調用它的原型鏈,這又是提供給我們通過Object.getPrototypeOf實現它。

那麼當我們有更好的選擇時,做Boy.prototype = new Human;模擬繼承的好處究竟是什麼呢?

回答

4

一個更好的選擇是創建一箇中間保持原型。

function extend(clazz, superclass) { 
    var intermediate = function() {}; 
    intermediate.prototype = superclass.prototype; 
    clazz.prototype = new intermediate(); 
    // Following line is optional, but useful 
    clazz.prototype.constructor = clazz; 
} 

這避免了不必要的複製,但仍意味着你不需要實例化一個對象,將在其構造函數做的工作。它還設置了原型鏈,以便您可以使用instanceof。它也不會導致一些繼承反模式可以超級原型污染。

爲了完整起見,您的子類應該在其構造函數中調用超類構造函數,您可以使用Superclass.call(this);來完成這個工作。

編輯:由於ES5,你可以用

Subclass.prototype = Object.create(Superclass.prototype); 

它做同樣的事情更換調用extend

+0

你得到的名字'問題intermediate'漂浮在你的原型鏈而不是名字'superclass' – Raynos 2011-05-13 11:46:38

+0

不是真的。首先沒有任何名字叫「中間」(這是一個匿名的構造函數 - 中間函數的作用域是擴展函數)。您可能會考慮這樣一個事實,即原型鏈中存在額外的對象是一個問題,但我認爲它實際上是正確的 - 額外的對象表示子類* class *。沒有它,沒有任何代表子類的東西。對象本身表示實例,對象的類應該位於原型鏈中。 – kybernetikos 2011-05-13 11:53:13

+0

@Adam你的正確的超類對象仍然在鏈中,除非你有它和clazz之間的中間。這種模式只適用於小型連鎖店。如果你使用這個模式鏈接4次,那麼你的原型鏈中有8個對象。這會顯着增加遍歷時間並減慢代碼的速度。 – Raynos 2011-05-13 12:01:01

4
  • 它正確地設置了原型鏈,這意味着instanceofisPrototypeOf()將工作
  • 它更簡潔

有簡單而醜陋的解決辦法,以防止一個構造函數執行其常規的初始化時你只是用它來創建一個原型對象。例如,您既可以檢查參數:

function Boy(name) { 
    if (arguments.length == 1) { 
     this.name = name; 
     // Do other initialization 
    } 
} 

...或將初始化到具有顯式調用一個單獨的方法:

function Boy(name) {} 

Boy.prototype.init = function(name) { 
    this.name = name; 
    // Do other initialization 
} 

這有你需要的明顯缺點要記得在創建新的Boy時致電init()

在ECMAScript中5個環境(當前版本的大多數瀏覽器,例如),更好的選擇可能的ECMAScript 5的Object.create(),它允許您創建直接從另一個對象繼承並建立原型鏈你的對象。這可以模仿(但只有約:看Object.defineProperty in ES5?)非ES5環境:

if (!Object.create) { 
    Object.create = function(o) { 
     function F() {} 
     F.prototype = o; 
     return new F(); 
    }; 
} 
+0

你在你的答案的最主要原因是正確的,但你的固定的問題也建議有時的原因是什麼,我會考慮的反模式。如果你發現.prototype = new Superclass();正在造成麻煩,正確的做法是使用更好的繼承系統,就像我在答案中提供的繼承系統,或者是建立在任何一個庫中的系統之一。 – kybernetikos 2011-05-13 11:45:10

+0

@TimDown將設置'.constructor'屬性修復instanceof和isPrototypeOf? – Raynos 2011-05-13 11:45:31

+0

@Raynos:不,構造函數屬性幾乎完全沒用。 – 2011-05-13 11:47:56

相關問題