在循环中创建闭包

Creating closures in loops

我正在学习闭包,并且了解它们是什么以及它们如何工作的基础知识。

我从 MDN 获得了以下代码并且知道解决方案是什么,因为它在同一篇文章中。我只是不明白这是怎么可能的:

<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>


function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}


function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();

我知道需要更改才能正常工作的代码部分是:

document.getElementById(item.id).onfocus = function() {

      showHelp(item.help);

    }

为什么在循环结束时所指向的文本是:所有元素的 Your age (you must be over 16)

我可以按照代码看到循环成功地正确地循环遍历了元素,但是我无法理解末尾所有元素指向的最后一项是如何 Your age ... 因为它用 onfocus = funtion()... 单独保存每一个,并且当时 item.help 的内容被传入并保存。

任何一步一步的解释都将极大地帮助我理解正在发生的事情。

如果我没理解错的话,你是在问为什么每次你关注一个元素时代码都指向 Your age (you must be over 16)

这不是真正的闭包问题,而是循环的问题是查找和设置要帮助的文本。

所以,每次你点击一个输入,函数都会运行,对吧?该函数循环遍历数组的每个成员,每次 3 个项目,并且在每次迭代中将 help 变量设置为当前数组项目。但是没有什么可以停止循环并且每次它运行它完全运行整个数组。所以它总是停在最后,这意味着 help 总是设置为最后一个变量。它永远不会在其他任何地方停止,所以每当你点击一个元素时,这个过程总是这样的。

  1. 点击元素(选择哪一个都无所谓)
  2. JavaScript 开始循环。
  3. 循环迭代到数组中的第一个元素并将项目变量设置为第一个元素。
  4. 循环迭代到数组中的第二个元素并将项目变量设置为第二个元素。
  5. 循环迭代到数组中的第三个元素并将项目变量设置为第三个元素。
  6. JavaScript 将 p#id 元素的 innerHTML 设置为项目变量 "help" 属性.
  7. 的文本

所以它总是显示第三个元素,因为它总是在第三个元素处停止。

Java脚本具有功能范围。这意味着函数中定义的所有变量都被提升到函数的顶部。 C# 或 Java 具有块级作用域,因此在循环中定义 item 是这些语言中的自然方式。在 JavaScript 中不是这种情况。为避免混淆,所有变量都应在函数顶部声明。 JS 解释器在计算函数时会将 var item 提升到函数的顶部。在循环中,您将 onfocus 设置为一个函数,该函数在 setupHelp 的父函数范围内具有引用 item 的变量。在循环结束时,item 具有最后一个索引项的分配值。因此,每次 onfocus 执行函数时,它都会引用 setupHelp 函数范围内分配的 item 值。为了创建一个 closure ,您需要在循环的每次迭代中执行一个匿名函数,这将创建一个新的功能范围。这个新作用域应该有一个 var,它被分配了来自外部函数的当前项。

此示例显示 for 循环的每次迭代中的功能范围闭包:

function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}


function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = (function() {
        var saveItem = item;
        return function () {
            showHelp(saveItem.help);
        }
    })();
  }
}

setupHelp();

示例fiddle:

https://jsfiddle.net/ohoy75kh/1/

https://jsfiddle.net/ohoy75kh/2/