Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
7
Help us understand the problem. What is going on with this article?
@u-akihiro

クロージャの例

More than 5 years have passed since last update.

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

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

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
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
7
Help us understand the problem. What is going on with this article?