LoginSignup
7
7

More than 5 years have passed since last update.

クロージャの例

Last updated at Posted at 2014-06-20

クロージャを理解するためのメモ書き

一つの変数を、複数のクロージャが補足している例。

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を補足して生成されたクロージャにも当然影響があるということ…。

流れとしてはこんな感じかな

  1. 変数itemを補足した1個目の無名関数がクロージャ化(以降、クロージャ1st)になる
  2. ループが進んで変数itemの値が更新される(クロージャ1stが補足している変数)
  3. 2個目の無名関数も変数itemを補足してクロージャ化(クロージャ2nd)になる
  4. ループが進んで変数itemの値が更新される(クロージャ1st, 2ndが補足している)
  5. 3個目の無名関数も変数itemを補足してクロージャ化(クロージャ3rd)になる
  6. ループ終了
7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7