2012-04-28 78 views
6

我覺得差已經點擊了我的頭,但我只是想確信的差異。在Javascript中,間「的Object.create」和「新」

在道格拉斯克羅克福德Prototypal Inheritance in JavaScript頁,他說

在原型系統中,對象從對象繼承。但是,JavaScript, 缺少執行該操作的操作員。相反,它 有一個新的操作者,以使得新的f()的產生一個新的對象,該對象從f.prototype 繼承。

我真的不明白他想說的那句話,所以我進行了一些測試。在我看來,關鍵的區別是,如果我創建一個基於純原型系統的另一個對象的對象,那麼所有的父父成員應在新對象的原型,而不是新的對象本身。

這裏的測試:

var Person = function(name, age) { 
     this.name = name; 
     this.age = age; 
} 
Person.prototype.toString = function(){return this.name + ', ' + this.age}; 

// The old way... 
var jim = new Person("Jim",13); 
for (n in jim) { 
    if (jim.hasOwnProperty(n)) { 
     console.log(n); 
    } 
} 
// This will output 'name' and 'age'. 

// The pure way... 
var tim = Object.create(new Person("Tim",14)); 
for (n in tim) { 
    if (tim.hasOwnProperty(n)) { 
     console.log(n); 
    } 
} 
// This will output nothing because all the members belong to the prototype. 
// If I remove the hasOwnProperty check then 'name' and 'age' will be output. 

我的理解是正確的,對象本身就爲大家測試時的區別僅變得明顯?

+3

請參閱http://stackoverflow.com/questions/4166616/understanding-the-difference-between-object-create-and-new-somefunction-in-j – 2012-04-28 08:14:57

+0

我昨天看過那個問題,我想,但是答案是我不清楚。現在,我已經完成我的測試,並輸入了我的問題,很明顯! – Jules 2012-04-28 08:21:16

回答

3

你的假設是正確的,但道格拉斯沒有多談另一種模式 - 原型可用於性能以及。你的個人類可能被寫爲:

var Person = function(name, age) { 
    this.name = name; 
    this.age = age; 
} 
Person.prototype.name = null; //default value if you don't init in ctor 
Person.prototype.age = null; 
Person.prototype.gender = "male"; 
Person.prototype.toString = function(){return this.name + ', ' + this.age;}; 

在這種情況下,遍歷這個類的一個實例的屬性,正如你在做例子,將不產生輸出的「性別」屬性。

編輯1: 在構造的名字和年齡的分配做使性能可見的hasOwnProperty(感謝@馬特提醒的這個我)。除非有人將其設置在實例上,否則未分配的性別屬性將不可見。

編輯2: 爲了進一步增加這一點,我提出了一個替代的遺傳模式 - 一個我親身用於非常大的項目:

var inherits = function(childCtor, parentCtor) { 
    function tempCtor() {}; 
    tempCtor.prototype = parentCtor.prototype; 
    childCtor.superclass = parentCtor.prototype; 
    childCtor.prototype = new tempCtor(); 
    childCtor.prototype.constructor = childCtor; 
}; 

var Person = function(name){ 
    this.name = name; 
} 
Person.prototype.name = ""; 
Person.prototype.toString = function(){ 
    return "My name is " + this.name; 
} 

var OldPerson = function(name, age){ 
    OldPerson.superclass.constructor.call(this); 
    this.age = age 
}; 
inherits(OldPerson, Person); 
OldPerson.prototype.age = 0; 
OldPerson.prototype.toString = function(){ 
    var oldString = OldPerson.superclass.toString.call(this); 
    return oldString + " and my age is " + this.age; 
} 

這是一小搓一個相當普遍的模式 - 父類通過「超類」屬性附加到孩子,允許您訪問孩子覆蓋的方法/屬性。從技術上講,你可以用Person取代OldPerson.superclass,但是這是不理想。如果您將OldPerson更改爲從Person以外的類繼承,那麼您必須更新對Person的所有引用。

編輯3: 只是把這個完整的圓,這裏是「繼承」功能的一個版本,它利用的Object.create和功能完全一樣,我前面描述:

var inherits = function(childCtor, parentCtor) { 
    childCtor.prototype = Object.create(parentCtor.prototype); 
    childCtor.superclass = parentCtor.prototype; 
}; 
3

編輯:這個答案最初是對@ jordancpaul的答案的迴應,他已經糾正了。我將留下我的答案部分,這有助於解釋原型屬性和實例屬性之間的重要區別:

在某些情況下,屬性在所有實例之間共享,每當您聲明屬性時都需要非常小心在原型上。考慮下面這個例子:

Person.prototype.favoriteColors = []; //Do not do this!

現在,如果您選擇使用Object.createnew創建一個新的Person實例,它不工作,你可能會想到...

var jim = new Person("Jim",13); 
jim.favoriteColors.push('red'); 
var tim = new Person("Tim",14); 
tim.favoriteColors.push('blue'); 

console.log(tim.favoriteColors); //outputs an array containing red AND blue! 

這沒有按」這意味着你不能在原型上聲明屬性,但是如果你這樣做了,那麼你和每個開發你的代碼的開發人員都需要知道這個陷阱。在這樣的情況下,如果你喜歡的原型是什麼原因聲明屬性,你可以這樣做:

Person.prototype.favoriteColors = null

並初始化爲在構造一個空數組:

var Person = function(name, age) { 
    ... 
    this.favoriteColors = []; 
} 

的使用此方法的一般規則是可以直接在原型上設置簡單文字屬性(字符串,數字,布爾值)的默認值,但是從Object繼承的任何屬性(包括數組和日期)應該設置爲null,然後進行初始化在構造函數中。

更安全的方法是僅在原型上聲明方法,並且始終在構造函數中聲明屬性。

無論如何,問題是關於的Object.create ...

傳遞的Object.create的第一個參數被設置爲新實例的原型。更好的用法是:

var person = { 
    initialize: function(name, age) { 
     this.name = name; 
     this.age = age; 
     return this; 
    }, 

    toString: function() { 
     return this.name + ', ' + this.age; 
    } 
}; 

var tim = Object.create(person).initialize("Tim",14); 

現在輸出將與第一個示例中的輸出相同。如你所見,這是一種與Javascript中更古典的OOP風格不同的哲學方法。使用Object.create,重點是從現有對象創建新對象,而不是構造函數。初始化然後成爲一個單獨的步驟。我個人對Object.create方法有着複雜的感受;繼承是非常好的,因爲第二個參數可以用來向現有原型添加其他屬性,但它也更加冗長,並且使得實例檢查不再有效(在此示例中,替代方法是檢查person.isPrototypeOf(tim)) 。

主要的原因,我說的Object.create是冗長的,因爲第二個參數,但也有一些有用的庫,在那裏,地址:

https://github.com/Gozala/selfish

https://github.com/Raynos/pd

(和其他人)

我希望比混亂更有啓發性!

+1

我只是寫了*非常*簡單的庫來幫助遵循更傳統的原型繼承方法的繼承,但延長「類」,當它使用的Object.create: https://github.com/mbrowne/simpleoo。 JS – 2012-11-11 04:29:08

+0

啊,犧牲品'Class.prototype.property = []'陷阱自己。我已經更新了我原來的職位多一點信息 - 或許你已經看過了這種模式。 – jordancpaul 2012-11-22 18:36:45