2015-08-08 48 views
0

我想了解如何以及爲什麼在Javascript中有用的原型。在我認爲我知道發生了什麼之後,我偶然發現原型只是一個對象,不能以我認爲的方式被許多對象「共享」。讓我用一個例子說明一下:原型繼承導致共享引用

var SpritePrototype = { 
    img: null, 
    pos_x: 0, 
    pos_y: 0, 

    draw: function(context2d) { 
     /*do stuff with the canvas 
      using "this" to refer to 
      the object this method is 
      being called on*/ 
    }, 
    //Some more member functions... 
} 

從,通常與原型友好的Javascript倡導的「從對象繼承的對象」的概念,我想我可能只是做:

var player = Object.create(SpritePrototype); 

但事實證明這種方法存在缺陷,因爲非函數字段將來自SpritePrototype,因爲玩家的原型完全是SpritePrototype。這意味着我無法從該原型創建更多對象,或者無功能字段會混淆在一起。

那麼Object.create有什麼意義,更重要的是,什麼是實現我想要做的正確方法?也就是說,我怎樣才能讓「玩家」得到一個字段的副本,並繼承其原型的功能?

再一次,我有興趣按照他們想要的方式做事。我總是可以手動模擬繼承或完全跳過它。我的問題的關鍵是要了解原型,以及它們如何以及何時有用,特別是在我的具體情況下。

回答

3

屬性值最初共享的,但是當停止對一個實例屬性寫入被共享(分配)到。此時,該實例獲取其屬性的版本。所以這是不完全正確的說值爲「共享」。它是共享最多隻在該實例上的屬性被分配到的時間點。

var SpritePrototype = { 
    img: 'img1' 
}; 
var sprite1 = Object.create(SpritePrototype); 
var sprite2 = Object.create(SpritePrototype); 

sprite1.img = 'img2';   // does NOT affect prototype or sprite2 
console.log(sprite2.img); 

< "img1" 

當引用img時,其值取自原型。然而,當img書面到,對實例創建一個新的屬性來保存新的價值,並且從那時起使用。

改變對原型屬性值的唯一方法是這樣做的明確:

SpritePrototype.img = 'img3'; 

這將改變img對於尚未通過分配給它定義的img自己的本地版本的所有實例。

+0

感謝您的澄清。你說的確實是對的。但是我不會說我的例子完全不正確。無論如何,這樣做的不良副作用是因爲img在sprite1和原型中都是這樣,所以屬性最終被創建兩次。 (編輯):或者這是使用原型的預期方式?某種「寫時複製」機制?我從未在其他地方見過這個。 – Setzer22

+0

它不是「不良」的副作用;這是一個理想的選擇。你不想在實例中共享一個可變屬性(除非它是「類級別」,又稱「靜態」屬性)。另一方面,如果你不需要屬性的實例級版本,則不需要保留一個版本。 – 2015-08-08 11:14:22

+0

原型繼承依賴於鏈接。當您基於原型創建對象時,除非您明確聲明新對象的值,否則其所有方法和屬性基本上都是指向原型對象的指針。如果你想實際複製屬性 - 如設置默認對象狀態 - 有不同的方法來做到這一點。 –

1

你的問題是,你已經在面向對象的設計方面定義了「靜態」成員。原型中的所有內容都在對象之間共享。函數實際上並沒有被複制到新創建的對象中,而是通過適當的「this」來調用它。

你應該初始化變量在構造函數中,類似這樣的

function Sprite(...) { 
    this.img = null; 
    ... 
} 

然後亞型,你應該使用的Object.create來創建一個原型繼承它的方法,所以

Player.prototype = Object.create(Sprite.prototype); 

最後,你可以調用父構造函數初始化變量

function Player(...) { 
    Sprite.call(this, ...); 
} 

PS。構造函數應該在Player.prototype分配之前。

PPS。欲瞭解更多信息請參見本原型https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create

+0

對,我明白了。什麼在擾亂我,爲什麼人們不斷提倡使用Object.create作爲構造函數的替代品。我已經閱讀了許多關於這個主題的文章,並且似乎總結出了構造函數+新的語法應該被省略,並由Object.create單獨取代。在這裏,你似乎混在一起... – Setzer22

+0

沒有得到這個答案。首先,OP沒有詢問是否創建子類型。其次,他大概知道如何在構造函數中使用'new';他在詢問**備選**模式,即如何使用Object.create創建對象。最後,他並不擔心功能/方法;他對非方法屬性感興趣,以及它們如何表現。 – 2015-08-08 11:10:04

+0

@ Setzer22那些主張完全避免使用'new'並使用Object.create'的人,如果有任何(per-instance)對象屬性,例如'的Object.create(美孚); Foo.init()'。 (有些庫創建了一個替代方法來做到這一點,例如'Foo.new()',它會在內部調用'Object.create()')。我個人認爲只是使用'new'可以正常工作,我不會購買參數反對它......你仍然可以以任何方式思考原型。 –