Heaven's Kitchen

○ JavaScriptでカプセル化

またしても野望の会ブログに反応してみる。

http://www.yabooo.org/archives/53

JavaScriptではC++やJavaと違ってprivateなインスタンス変数や,メソッドが定義できないのでなんとかしましょうという話。

同じようなことがPerlでもやられていて,Perl Hacksでは裏返しオブジェクトというのが紹介されています。上記のエントリーと同じ発想で,クロージャを使うわけですがオブジェクトの保存場所が微妙に違います。JavaScriptでやるとこんな感じ?

var Class = {
    'create': function (tmpl) {
	var repos = [];
	var klass = function () {
	    this._id = repos.length;
	    repos.push({});
	    tmpl.initialize.apply(repos[this._id], arguments);
	};
	for (var i in tmpl) {
	    if (i == 'initialize') continue;
	    if (typeof tmpl[i] == 'function')
		klass.prototype[i] = function () {
		    return tmpl[i].apply(repos[this._id], arguments);
		};
	}
	klass.extend = function (obj) {
	    for (var i in obj) {
		klass.prototype[i] = function () {
		    return obj[i].apply(repos[this._id], arguments);
		};
	    }
	};
	return klass;
    }
};

var Foo = Class.create({
	'initialize' : function () {
	    this.x = 100;
	},
	'getX' : function () {
	    return this.x;
	}
    });

var foo = new Foo;

print(foo.getX()); // 100
print(foo.x);      // undefined

メソッドは全部パブリックだし,いろいろ手抜きだけどとりあえず隠蔽はできてます。クラスのコンストラクタそのものをクロージャにしてしまうとこがポイントですね。

メソッドはprototype経由で呼び出されるので,オブジェクトをnewする度に発生する,メモリ消費のオーバヘッドもありません(たぶん

さらに,あとからメソッドを追加したくなった場合は

Foo.extend({
	'printX' : function () {
	    print(this.x);
	}
    });

foo.printX(); // 100

とかできて,うれしい気持ち…。

Valid XHTML 1.0! Valid CSS!