由於在後JS - Why use Prototype?,原型的美女 一個提到的是,你只需要初始化原型成員只有一次, 而在構造函數中成員每個實例創建。實際上,您可以直接訪問原型而不創建新對象。
Array.prototype.reverse.call([1,2,3,4]);
//=> [4,3,2,1]
function add() {
//convert arguments into array
var arr = Array.prototype.slice.call(arguments),
sum = 0;
for(var i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
add(1,2,3,4,5);
//=> 15
在你的功能,還有額外的開銷,以創建一個完全新的 動物和羊每一個構造函數被調用的時間。一些成員例如Animal.name是用每個實例創建的,但我們知道 Animal.name是靜態的,所以最好實例化一次。由於您的代碼意味着Animal.name 應該在所有動物中都是相同的,所以只需更新Animal.prototype.name,如果我們將 移動到原型,就很容易更新所有實例的Animal.name。
考慮這個
var animals = [];
for(var i = 0; i < 1000; i++) {
animals.push(new Animal());
}
功能繼承/模塊模式
function Animal() {
return {
name : 'Generic',
updateName : function(name) {
this.name = name;
}
}
}
//update all animal names which should be the same
for(var i = 0;i < animals.length; i++) {
animals[i].updateName('NewName'); //1000 invocations !
}
與原型
Animal.prototype = {
name: 'Generic',
updateName : function(name) {
this.name = name
};
//update all animal names which should be the same
Animal.prototype.updateName('NewName'); //executed only once :)
如以上那樣,您的當前模塊模式,我們在 更新屬性失去效益分析這應該是所有成員的共同點。
如果您concered約知名度,我會用你目前正在使用封裝的私有成員也使用 priviledged members用於訪問這些成員,他們應該需要達到相同的模塊化方法。 特權會員是提供訪問私有變量的接口的公共成員。最後添加通用成員到原型。
當然要走這條路,你需要跟蹤這個。 的確,在您的實現有
- 沒有需要通過$ .proxy跟蹤this指針(FN,這一點)在回調中
- 沒有更多的變種是=這等。事件處理程序等等。每當我看到一個'this'時,我知道這是傳遞給回調的上下文,但它不是我正在追蹤的知道我的對象實例的東西。
,但你每次都將相較於使用一些原型繼承佔用更多的內存時間創建一個非常大的對象。
事件代表團作爲比喻
的類比通過使用原型性能獲得通過操縱DOM在使用事件代表團提高了性能。Event Delegation in Javascript
比方說你有一個大的雜貨店list.Yum。
<ul ="grocery-list">
<li>Broccoli</li>
<li>Milk</li>
<li>Cheese</li>
<li>Oreos</li>
<li>Carrots</li>
<li>Beef</li>
<li>Chicken</li>
<li>Ice Cream</li>
<li>Pizza</li>
<li>Apple Pie</li>
</ul>
比方說,你想記錄你點擊的項目。 一個實現將附加一個事件處理程序每個項目(壞),但如果我們的名單很長,會有很多事件的管理。
var list = document.getElementById('grocery-list'),
groceries = list.getElementsByTagName('LI');
//bad esp. when there are too many list elements
for(var i = 0; i < groceries.length; i++) {
groceries[i].onclick = function() {
console.log(this.innerHTML);
}
}
另一個實現將附加一個事件處理到父(好),並有一個父處理所有的點擊。 正如你可以看到這是類似於使用常見功能的原型,並顯著提高性能
//one event handler to manage child elements
list.onclick = function(e) {
var target = e.target || e.srcElement;
if(target.tagName = 'LI') {
console.log(target.innerHTML);
}
}
重寫使用的多功能組合/原型繼承
我認爲功能/原型繼承的組合可以寫成以一種容易理解的方式。 我已經使用上述技術重寫了您的代碼。
var Animal = function() {
var helloCount = 0;
var self = this;
//priviledge methods
this.AnimalHello = function() {
helloCount++;
console.log(self.Name + ' says hello (animalHello)');
};
this.GetHelloCount = function (callback) {
callback.call(null, helloCount);
}
};
Animal.prototype = {
Name: 'Generic',
IsAnimal: true
};
var Sheep = function (name) {
var sheep = new Animal();
//use parasitic inheritance to extend sheep
//http://www.crockford.com/javascript/inheritance.html
sheep.Name = name || 'Woolie'
sheep.SheepHello = function() {
this.AnimalHello();
var self = this;
this.GetHelloCount(function(count) {
console.log('i (' + self.Name + ') have said hello ' + count + ' times (sheepHello anon callback)');
});
}
return sheep;
};
Sheep.prototype = new Animal();
Sheep.prototype.isSheep = true;
var sheepie = new Sheep('Sheepie');
var lambie = new Sheep('Lambie');
sheepie.AnimalHello();
sheepie.SheepHello();
lambie.SheepHello();
結論
外賣是同時使用原型和功能繼承自己的優點既解決性能和可視性問題。 最後,如果你工作在一個小JavaScript應用程序,並且這些性能問題不是一個問題, 那麼你的方法是可行的辦法。
您的'Animal.prototype.updateName('NewName');'不修改所有動物 - 它只修改所有它們繼承的'Generic'名稱。順便說一句,你的組合腳本包含一些不好的做法,在所有情況下都不起作用,並且仍然使用'Sheep'的功能/寄生繼承。它沒有顯示OP的一個優點... – Bergi
是的,你對'Animal.prototype.updateName('NewName')'只更新'Generic' name'是正確的,但這是預期的效果。所有羊都有一個通用名稱(因爲它們仍然是動物)和他們自己的名字。 – AnthonyS
隨時更新我的代碼。我的觀點是,當前使用$ .extend會重新生成諸如「Animal.Name」或「Animal.IsAnimal」之類的常用屬性,並且某些屬性(如Name)會在稍後被覆蓋。爲了充分利用$ .extend,我認爲應該使用代碼,以便擴展只發生一次以構建基本的Sheep對象,然後應該使用構造函數/函數來初始化值(例如'makeSheep'函數描述),而不是每次都進行冗餘擴展。 – AnthonyS