クロージャを理解するためのメモ書き
一つの変数を、複数のクロージャが補足している例。
function myFunc() {
var name;
func = {
input: function(n) {
name = n
},
output: function() {
console.log(name);
}
}
return func;
}
func = myFunc();
// 2つのクロージャが変数nameを補足している。
// そのため、関数inputで変数nameの値を操作すると
// 関数outputの出力結果も当然ながら変わる。
func.input('foo');
func.output();
// ブラウザのコンソールに、文字列fooが出力される
ループとクロージャ
ループの中でクロージャを使うときに遭遇する不具合(というか、言語仕様の理解不足)の例と対策。
良いサンプルが思いつかなかったので、Mozillaが公開しているコードを引用。
Mozilla
期待通りに動いてくれない例
まずは、うまく動いてくれないコードから。
このコードは、それぞれの入力フォームにフォーカスがあうと、対応したメッセージが表示されるというもの。
しかし、実際に動かしてみると分かるのだけど、どのフォームを選択しても同じメッセージしか表示されない。
(具体的には、どれを選択しても「あなたの年齢 (17 歳以上)」というメッセージが出てくる)
<p id="help">ここにヘルプが表示されます</p>
<p>Eメール: <input type="text" id="email" name="email"></p>
<p>名前: <input type="text" id="name" name="name"></p>
<p>年齢: <input type="text" id="age" name="age"></p>
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'あなたのEメールアドレス'},
{'id': 'name', 'help': 'あなたのフルネーム'},
{'id': 'age', 'help': 'あなたの年齢 (17 歳以上)'}
];
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
}
}
}
setupHelp();
解説
*それは違うよ!!ってところがあるかもしれません。
クロージャの話をしているので、どこが悪いかってのは大体予想が出来てしまうと思うけど。
悪さをしているのは、この部分。
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
}
}
ここで重要になってくるのは、どの変数が補足されているのか、そしていくつのクロージャが生成されているか、だ。
まず、クロージャの生成がされているコードを抜き出してみる。
// 変数itemを無名関数が補足することでクロージャが生成される。
// もとのコードでは、クロージャの生成がループの中で行われているので
// ループの回数分、変数itemを補足したクロージャが生成される
var item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
}
ループのたびに変数itemを補足したクロージャが生成されるのは別に良いのだけど
問題は、ループが進む事に変数itemの値がくるくると変更されているという点。
変数itemの値が変わるということは、それを変数itemを補足して生成されたクロージャにも当然影響があるということ…。
流れとしてはこんな感じかな
- 変数itemを補足した1個目の無名関数がクロージャ化(以降、クロージャ1st)になる
- ループが進んで変数itemの値が更新される(クロージャ1stが補足している変数)
- 2個目の無名関数も変数itemを補足してクロージャ化(クロージャ2nd)になる
- ループが進んで変数itemの値が更新される(クロージャ1st, 2ndが補足している)
- 3個目の無名関数も変数itemを補足してクロージャ化(クロージャ3rd)になる
- ループ終了