2007年11月22日木曜日

プライベートメンバをサブクラス間で共有してしまう問題

以下のコードのようにローカル変数をオブジェクトに閉じこめる場合、期待した結果と違う結果となることがあります。
この例を見ると少し驚きます。
function Shape() {
var area = 50;
this.setArea = function(a) {area = a;};
this.getArea = function() {return area;};
}

function Square() {
}

Square.prototype = new Shape();

var shape1 = new Square();
var shape2 = new Square();

shape1.setArea(100);

alert(shape1.setArea());
/////100

alert(shape2.setArea());
/////100
/////??...
このように、あるクラスを継承したクラスが 2つの中にローカル変数を閉じ込める場合、ローカル変数がサブクラス間で共有されてしまいます。。

解決法

スーパークラスのコンストラクタをサブクラスのコンストラクタから呼ぶことです。
これにより各々のサブクラスはプライベート変数のローカルコピーを作成するので、サブクラス間でデータが共有されることが確実になくなります。
function Shape() {
var area = 50;
this.setArea = function(a) {area = a;};
this.getArea = function() {return area;};
}

function SquareB() {
Shape.call(this);
}

var shape1B = new SquareB();
var shape2B = new SquareB();

shape1B.setArea(100);

alert(shape1.setArea());
/////100

alert(shape2.setArea());
/////50
とするか、
function Shape() {
var area = 50;
this.setArea = function(a) {area = a;};
this.getArea = function() {return area;};
}

function SquareA() {
this.Shape = Shape;
this.Shape();
}

var shape1A = new SquareA();
var shape2A = new SquareA();

shape1A.setArea(100);

alert(shape1.setArea());
/////100

alert(shape2.setArea());
/////50
とすると成功しますね。

参考:Object-Oriented Programming with JavaScript, Part II: Methods