【JavaScript】クロージャをファクトリーパターンとして理解する

「クロージャって何?」って聞かれると答えるのって難しくありません?
仕組みとか用途とかってJavaやPHPで例えるのが難しくて
結局使わずにナァナァなコーディングをしてしまったり。。。
その結果、どこにスコープが当たっているのか分からない変数だらけで、
functionの中にfunctionがある入れ子だらけのコーディングになってしまう。。。
そんな毎日を日々過ごしているとかいないとか。

ということでワタクシ宣言いたします!!
今後一切クロージャを使いません!!
ファクトリー(あるいはコンストラクタ)という名のクロージャを使います!!
・・・はい、引き続きクロージャを使うんです(汗)
けど考え方を改めようかと。

どう考え方を改めるのか?
順を追って説明したいと思います。
まずは単純なクロージャの例から。

// 通常のクロージャ
var fnc = (function() {
	var i = 1;
	return function() {
		console.log(i++);
	};
})();
console.log("---fnc---");
fnc(); // 1
fnc(); // 2
fnc(); // 3

無名関数を即時実行して、関数を返却します。
すると変数iが閉塞されてステートフルな関数fnc()の出来上がりです。

分かってしまえばなんてことはないクロージャなんですが
無名関数のスコープにある変数iが、返却した関数fnc()から参照できるって感覚が
どうも違和感を覚えて、頭の中がモヤモヤして、結局のところ
「クロージャって何だっけ?」とか「クロージャっていつ使うんだっけ?」とか
なってしまうんじゃないかと。僕の場合の話ですけどw

つまりクロージャを「関数を返す関数」みたいに覚えるのが
ダメって結論になりました。
既存の概念に合わせると「プライベート変数を参照するメソッド」ってことですよね?
関数の返却自体も複数セットにしてしまえば、
それはもはやオブジェクトと呼べるべき存在。
というかオブジェクトとして返却すべき。
ってことで次の例です。

// 複数個の関数を返却するクロージャ
var obj = (function() {
	var j = 1;
	return {
		m1:function() {
			console.log("m1 : " + j);
			j++;
		},
		m2:function() {
			console.log("m2 : " + j);
			j *= 2;
		}
	};
})();
console.log("---obj---");
obj.m1(); // m1 : 1
obj.m1(); // m1 : 2
obj.m1(); // m1 : 3
obj.m2(); // m2 : 4
obj.m2(); // m2 : 8
obj.m2(); // m2 : 16

もうここまで来ると最初の無名関数はファクトリーメソッドなワケで。
ってことは無名じゃなくて有名(?)にした方がいいですね。
だってオブジェクトを量産できるんですもの。
有名ってことで、最後の例では変数などなど有名どころを使います。

// ファクトリーメソッドのような振る舞い
var SaiyaJinFactory = function() {
	var combatPower = 5; // 「戦闘力・・・たったの5か・・・ゴミめ」
	return {
		shugyo:function() {
			combatPower++;
			console.log("戦闘力 : " + combatPower);
		},
		super:function() {
			combatPower *= 2;
			console.log("戦闘力 : " + combatPower);
		}
	};
};
var bardock = SaiyaJinFactory(); // カカロット父
console.log("---bardock---");
bardock.shugyo(); // 戦闘力 : 6
bardock.shugyo(); // 戦闘力 : 7
bardock.shugyo(); // 戦闘力 : 8
var cacarot = SaiyaJinFactory(); // クリリンのことかー!!!
console.log("---cacarot---");
cacarot.shugyo(); // 戦闘力 : 6
cacarot.shugyo(); // 戦闘力 : 7
cacarot.shugyo(); // 戦闘力 : 8
console.log("---bardock---");
bardock.super(); // 戦闘力 : 16
bardock.super(); // 戦闘力 : 32
bardock.super(); // 戦闘力 : 64
console.log("---cacarot---");
cacarot.super(); // 戦闘力 : 16
cacarot.super(); // 戦闘力 : 32
cacarot.super(); // 戦闘力 : 64

あくまでプログラムの例なので「戦闘力よ〜わっ!」とか
ツッコミは無しの方向でお願いします。
bardockとcacarotでそれぞれヒープ領域を確保する(と思う)ので
無事に変数combatPowerはオブジェクトインスタンス内のスコープに納まりました。

個人的にはprototypeを定義する書き方よりも
JSON形式で書いてある方が読みやすいと思うので
今後クラス定義的なコーディングは、この方式のクロージャで一本化してみます。
それがダメならまた考えますw

・・・あれ?返却するオブジェクトの中に変数入れるのと何が違うんだっけ?
あと、余談ですがキャメルケースよりスネークケースの方が好きです。

コメントを残す

メールアドレスが公開されることはありません。