これらを他のものよりも使用する利点があるかどうか疑問に思っています。
コンストラクターアプローチ:
var Class = function () {
this.calc = function (a, b) {
return a + b;
};
};
プロトタイプアプローチ:
var Class = function () {};
Class.prototype.calc = function (a, b) {
return a + b;
};
プロトタイプを使用すると、メソッド定義がクラスから分離されるため、最初のアプローチだけでこれを使用する特定の理由があるかどうかはわかりません。
また、関数定義を使用するだけでなく、関数リテラルを使用して「クラス」を定義する利点もあります。
var Class = function () {};
対
function Class () {};
ありがとう!
プロトタイプチェーンを介して継承するメソッドは、すべてのインスタンスに対して普遍的に変更できます。次に例を示します。
function Class () {}
Class.prototype.calc = function (a, b) {
return a + b;
}
// Create 2 instances:
var ins1 = new Class(),
ins2 = new Class();
// Test the calc method:
console.log(ins1.calc(1,1), ins2.calc(1,1));
// -> 2, 2
// Change the prototype method
Class.prototype.calc = function () {
var args = Array.prototype.slice.apply(arguments),
res = 0, c;
while (c = args.shift())
res += c;
return res;
}
// Test the calc method:
console.log(ins1.calc(1,1,1), ins2.calc(1,1,1));
// -> 3, 3
メソッドが両方のインスタンスにどのように適用されるかに注目してください。これは、ins1
とins2
が同じcalc()
関数を共有しているためです。構築中に作成されたパブリックメソッドでこれを行うには、作成された各インスタンスに新しいメソッドを割り当てる必要がありますが、これは厄介なタスクです。これは、ins1
とins2
が独自に作成したcalc()
関数を持つためです。
コンストラクター内でメソッドを作成する別の副作用は、パフォーマンスの低下です。コンストラクター関数を実行するたびに、各メソッドを作成する必要があります。プロトタイプチェーン上のメソッドは、一度作成され、各インスタンスによって「継承」されます。コインの裏側では、パブリックメソッドは「プライベート」変数にアクセスできますが、これは継承されたメソッドでは不可能です。
function Class() {}
対var Class = function () {}
の質問に関しては、前者は実行前に現在のスコープの先頭に「引き上げられ」ます。後者の場合、変数宣言は引き上げられますが、割り当ては引き上げられません。例えば:
// Error, fn is called before the function is assigned!
fn();
var fn = function () { alert("test!"); }
// Works as expected: the fn2 declaration is hoisted above the call
fn2();
function fn2() { alert("test!"); }
プロトタイプアプローチの利点は効率です。すべてのClass
オブジェクト間で共有されるcalc()
関数オブジェクトが1つあります(つまり、Class
コンストラクターを呼び出して作成されたオブジェクトを意味します)。もう1つの方法(コンストラクター内でメソッドを割り当てる)は、Class
コンストラクターを呼び出す際により多くのメモリを使用し、より多くの処理時間を費やして、Class
オブジェクトごとに新しい関数オブジェクトを作成します。ただし、このアプローチには利点があります。calc()
メソッドはコンストラクター内のローカル変数にアクセスできます。
function Class() {
var calcCallCount = 0;
this.calc = function (a, b) {
++calcCallCount;
alert("Calc called " + calcCallCount + " times");
return a + b;
};
};
var Class = function() {...}
対function Class() {...}
に関して、後者は、関数に名前があることを意味するため、一般的に後者が好まれます。これは、デバッグ時に役立つ場合があります。もう1つの違いは、後者のバージョン(関数宣言)がホイストされていることです。つまり、定義されているスコープ内ではなく、定義の直後。ただし、 一部の人々 は、前者(()関数式)をどこでも使用することを好みます。
var YourClass = function(){
var privateField = "somevalue";
this.publicField = "somevalue";
this.instanceMethod1 = function(){
//you may access both private and public field from here:
//in order to access public field, you must use "this":
alert(privateField + "; " + this.publicField);
};
}
YourClass.prototype.instanceMethod2 = function(){
//you may access only public field 2 from this method, but not private fields:
alert(this.publicField);
//error: drawaback of prototype methods:
alert(privateField);
};
プロトタイプメソッドの利点:
プロトタイプを介してメソッドを定義すると、それらはすべてのYourClassインスタンス間で共有されます。その結果、そのようなインスタンスの合計サイズは、コンストラクターでメソッドを定義する場合よりも小さくなります。プロトタイプによるメソッド定義がhtmlページの合計サイズを小さくし、その結果、読み込み速度を低下させる方法を示すテストがあります。
プロトタイプを介して定義されたメソッドのもう1つの利点-継承クラスを使用する場合、そのようなメソッドをオーバーライドでき、派生クラスのオーバーライドされたメソッドでは、同じ名前の基本クラスのメソッドを呼び出すことができますが、メソッドはコンストラクターで定義されます。これはできません。