要するにパフォーマンスの問題だと思います。わずかなパフォーマンスの低下があるとおっしゃいましたが、これは実際にはアプリケーションの規模 (羊 2 匹 vs 羊 1000 匹) によって異なります。プロトタイプの継承を無視してはなりません。関数型の継承とプロトタイプの継承を組み合わせて使用することで、効果的なモジュール パターンを作成できます。
投稿で述べたように、JS - なぜプロトタイプを使用するのですか? 、プロトタイプの利点の 1 つは、コンストラクター内のメンバーがインスタンスごとに作成されるのに対し、プロトタイプ メンバーを 1 回だけ初期化する必要があることです。実際、新しいオブジェクトを作成しなくても、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 と羊を作成するための余分なオーバーヘッドがあります。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 :)
上記のように、現在のモジュール パターンでは、すべてのメンバーに共通する必要があるプロパティを更新する効率が失われます。
可視性について懸念がある場合は、現在プライベートメンバーをカプセル化するために使用しているのと同じモジュラーメソッドを使用しますが、
到達する必要がある場合は、これらのメンバーにアクセスするために特権メンバーも使用します。特権メンバーは、プライベート変数にアクセスするためのインターフェイスを提供するパブリック メンバーです。最後に、共通メンバーをプロトタイプに追加します。
もちろん、このルートをたどるには、これを追跡する必要があります。あなたの実装には本当です
-
コールバックで $.proxy(fn, this) を介して「this」ポインターを追跡する必要はありません
-
イベントハンドラーなどで var that = this などはもうありません。「this」が表示されるたびに、コールバックに渡されているのはコンテキストであることがわかります。これは、オブジェクトインスタンスを知るために追跡しているものではありません。
、しかし、毎回非常に大きなオブジェクトを作成しているため、プロトタイプの継承を使用する場合と比較して、より多くのメモリを消費します。
類推としてのイベント委任
プロトタイプを使用してパフォーマンスを向上させることは、DOM を操作するときにイベント委任を使用してパフォーマンスを向上させることに例えられます。JavaScript でのイベント委任
大規模な食料品リストがあるとしましょう。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>
クリックしたアイテムをログに記録したいとしましょう。実装の 1 つは、すべての item(bad)にイベント ハンドラーをアタッチすることですが、リストが非常に長い場合、管理するイベントが多くなります。
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);
}
}
もう 1 つの実装は、1 つのイベント ハンドラーを親 (良い) にアタッチし、その 1 つの親にすべてのクリックを処理させることです。ご覧のとおり、これは一般的な機能のプロトタイプを使用することに似ており、パフォーマンスが大幅に向上します
//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 アプリケーションで作業していて、これらのパフォーマンスの問題が問題にならない場合、この方法は実行可能なアプローチになります。