2011-08-07 32 views
0

因此,在回答本網站的另一個問題時,我寫了一個類來爲某人創建一個JavaScript中的BitArray實例。我發佈的代碼是這樣的:如果該類永遠不會被繼承,JavaScript中的原型真的很重要嗎?

var foo = function(param1) { 
    this._member = param1; 
    this.getMember = function() { return this._member; }; 
    this.setMember = function(val) { this._member = val; }; 
    this.alertMember = function() { alert(this._member); }; 
    foo.STATIC_CONSTANT = "some value"; 
} 

一個我收到的意見,是一個相當嚴厲:「你絕對應該添加方法foo.prototype,而不是這個。」如果我對別人的批評,並把它應用到上面,它把代碼變成:

var foo = function(param1) { 
    this._member = param1; 
} 
foo.prototype.getMember = function(){ return this._member; }; 
foo.prototype.setMember = function(val) { this._member = val; }; 
foo.prototype.alertMember = function() { alert(this._member); }; 
foo.STATIC_CONSTANT = "some value"; 

我的問題是,這裏有什麼之間假設富的兩種方法將永遠不會被繼承的區別?爲了清楚起見,我不是在主張編寫草率的代碼 - 我相信評論員的觀點是讓我sl - 不馴 - 但是當我意識到他的正確性時,它確實讓我思考 - 爲什麼我應該原型,創建一個有效密封的課程?

這裏有一些性能或功能性後果嗎?或者當一個類不會被分類時使用原型不相關?

+0

只是澄清:我沒有說「方法不應該在原型之外」,這絕對不是這種情況。有很多有效的用例,你想要在對象實例上有方法,而不是在對象的原型上。 *這不是其中的一個。 –

+0

@Daniel Baulig - 誹謗糾正:) – Brian

回答

4

有各種原因。首先,儘管我認爲JavaScript中的類的說法是誤導性的。 JavaScript中沒有類,只有原型。

讓我們分析第一種方法實際發生的情況以及第二種方法發生的情況。當第一種方法的代碼運行時,它定義了一個函數,當它作爲構造函數被調用時,將創建一堆其他函數並將它們分配給新創建的對象的成員。您應該意識到每次調用該函數時都會執行此操作。所以如果你調用這個函數一次,你將有一個總共有4個函數的對象。如果您調用該函數兩次,則會有兩個對象共有8個函數,依此類推。每次使用此構造函數創建一個新對象時,都將爲其創建一組完整的函數。每個功能都需要內存。此外,現代JIT編譯器試圖在代碼中發現熱點可能無法將這些函數識別爲熱點,因爲不是有一個被稱爲多次的函數,而是有幾個函數,每個函數只被調用一次。此外,即使JIT能夠將這些功能看作熱點,它也必須分別編譯這些功能,這將需要額外的工作。

這就是說我相信假設「foo永遠不會被分類」應該是無效的。你不能假設其他人不想從你的對象繼承,或者想要增加對象的原型。目前這是不可能的。想象一下,我想改變foo.STATIC_CONSTANT。我不能,因爲每次我構造一個新的foo時,它都會重寫我更改後的foo.STATIC_CONSTANT。對於我想要做的任何其他增強,情況也是如此。

所有這些問題都不出現在第二個,正確的面向對象的javascript模式。

+0

+1以獲得全面的答案。關於子類,在這個問題的範圍內,我問,假設是否有效,忽略原型的後果是什麼?儘管就每個實例的性能缺陷和優化缺失而言,你的回答相當充分。 – Brian

+0

+1,第一個例子中發生了什麼的很好的解釋。 – nickf

2

是的,有區別。

在第一個示例中,每次創建該類的新實例時,都會重新定義其方法。這使得它在速度和內存方面效率非常低。

舉例說明:

function C1() { 
    this.foo = function() { }; 
} 

function C2() {} 
C2.prototype.foo = function() {}; 

c1a = new C1(); 
c1b = new C1(); 

c2a = new C2(); 
c2b = new C2(); 

c1a.foo === c1b.foo; // false 
c2a.foo === c2b.foo; // true 
+0

其實,他們不*重新定義*(並且它不是類,但這是另一個問題)。方法從頭開始定義。 – whitequark

+0

好吧,但術語確實讓Javascript OOP感到困惑。 – nickf

+0

好的,我們可能會離開課程,但方法不會被重新定義。它們在執行構造函數時並不在'this'對象上定義,並且如果它們是在原型上定義的,則它們可以用它訪問。 – whitequark

1

在這裏,你是對數組的每個實例從頭開始創建三個功能。這有一些內存成本;可以討論這個代價是否很大,但是如果你嘗試創建這個數組的數千個實例,你會注意到這個差異。

對象的創建速度也會稍微慢一些,但最近優化後的運行時間(比如V8)並不多。