TOTAL ACCESS 無料カウンター  UNIQUE ACCESS 無料カウンター

2008年7月24日木曜日

closure を知らなかった件

今日の今日まで closure の存在を知りませんでした。ゴメンナサイ。もしかしたら今まで書いたコードの中にバグがあったかもしれません。しかしながら、インスタンス変数でない限り、 function 内で宣言した変数なんてものは月下美人のごとく一夜限りの命かと思っていましたが、そんなことはないというのは非常に奇妙に感じます。

そんなこんなで closure を知らなかった僕のような人を出来るだけ減らすために今回は closure に関して説明をしてみます。

クロージャのサンプルコードを書いてみます
---
var hoge = function(){
  var i = 0;
  var foo = function(){
    i++;
    return i;
  };
  return foo;
};

var bar = hoge(); // (1)
alert(bar()); // 結果:1 (2)
alert(bar()); // 結果:2 (3)
alert(bar()); // 結果:3
---
まぁこんな感じでしょう。

・ (1) で hoge の結果が bar に入ります。
・ hoge の内部で return foo となってるので foo への関数ポインタが突っ込まれます。
・ なので bar() とやると foo() とやってるのと同じことになります。
・ i=0 で宣言されてるので (2) で 1 が返るのは当たり前。
・ でもね
・ (3) で i がさらにインクリメントされます! (当たり前じゃーんとか言う人はスルーしてね

どういうことかというと (1) でbar に hoge() の結果が突っ込まれたときに foo が定義され、同時に i も定義されています。 foo 内の i はhoge のスコープでで定義されています。i の状態がbar()が終わっても保持されているということは、つまりは foo が foo の 定義時の定義スコープを保持するわけです(やはり説明が難しいなこれ)。んで、この関数本体(foo)と定義スコープの対を「クロージャ(closure)」、ここでいう hoge を「エンクロージャ(enclosure)」と呼ぶそうです。

これって結構重要で、便利に使う方法を探すのは良いですけど、逆にバグの温床になりそうな悪寒がします。使い捨てだと思っていた関数内で宣言した変数が状態を保持するとなると、予期せぬ結果が返ったりするかもしれません。

いままでこんなこと知らなかったことにちと反省。しかしこれは奇妙だ。