在循环中创建闭包
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
总是设置为最后一个变量。它永远不会在其他任何地方停止,所以每当你点击一个元素时,这个过程总是这样的。
- 点击元素(选择哪一个都无所谓)
- JavaScript 开始循环。
- 循环迭代到数组中的第一个元素并将项目变量设置为第一个元素。
- 循环迭代到数组中的第二个元素并将项目变量设置为第二个元素。
- 循环迭代到数组中的第三个元素并将项目变量设置为第三个元素。
- JavaScript 将
p#id
元素的 innerHTML 设置为项目变量 "help" 属性. 的文本
所以它总是显示第三个元素,因为它总是在第三个元素处停止。
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/
或
我正在学习闭包,并且了解它们是什么以及它们如何工作的基础知识。
我从 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
总是设置为最后一个变量。它永远不会在其他任何地方停止,所以每当你点击一个元素时,这个过程总是这样的。
- 点击元素(选择哪一个都无所谓)
- JavaScript 开始循环。
- 循环迭代到数组中的第一个元素并将项目变量设置为第一个元素。
- 循环迭代到数组中的第二个元素并将项目变量设置为第二个元素。
- 循环迭代到数组中的第三个元素并将项目变量设置为第三个元素。
- JavaScript 将
p#id
元素的 innerHTML 设置为项目变量 "help" 属性. 的文本
所以它总是显示第三个元素,因为它总是在第三个元素处停止。
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/
或