Nic*_*ick 23 javascript closures loops
我在闭包上阅读Mozilla开发者的网站,我在他们的例子中注意到常见的错误,他们有这样的代码:
<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>
Run Code Online (Sandbox Code Playgroud)
和
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
并且他们说对于onFocus事件,代码只会显示最后一项的帮助,因为分配给onFocus事件的所有匿名函数都有一个围绕'item'变量的闭包,这是有道理的,因为在JavaScript变量中没有块范围.解决方案是使用'let item = ...'代替,然后它有块范围.
但是,我想知道为什么你不能在for循环之上声明'var item'?然后它具有setupHelp()的范围,并且每次迭代都会为它分配一个不同的值,然后将其作为闭包中的当前值捕获...对吗?
whi*_*awk 26
因为在item.help
评估时,循环将完全完成.相反,你可以用一个闭包来做到这一点:
for (var i = 0; i < helpText.length; i++) {
document.getElementById(helpText[i].id).onfocus = function(item) {
return function() {showHelp(item.help);};
}(helpText[i]);
}
Run Code Online (Sandbox Code Playgroud)
JavaScript没有块范围,但它具有函数范围.通过创建闭包,我们将helpText[i]
永久捕获引用.
End*_*der 20
闭包是函数和该函数的作用域环境.
在这种情况下,它有助于理解Javascript如何实现范围.事实上,它只是一系列嵌套的词典.考虑以下代码:
var global1 = "foo";
function myFunc() {
var x = 0;
global1 = "bar";
}
myFunc();
Run Code Online (Sandbox Code Playgroud)
当程序开始运行时,你有一个范围字典,即全局字典,它可能在其中定义了许多内容:
{ global1: "foo", myFunc:<function code> }
Run Code Online (Sandbox Code Playgroud)
假设你调用myFunc,它有一个局部变量x.为此函数的执行创建了一个新范围.函数的本地范围如下所示:
{ x: 0 }
Run Code Online (Sandbox Code Playgroud)
它还包含对其父作用域的引用.所以函数的整个范围如下所示:
{ x: 0, parentScope: { global1: "foo", myFunc:<function code> } }
Run Code Online (Sandbox Code Playgroud)
这允许myFunc修改global1.在Javascript中,每当您尝试为变量赋值时,它首先检查变量名称的本地范围.如果未找到,则检查parentScope和该范围的parentScope等,直到找到该变量.
闭包实际上是一个函数加上一个指向该函数范围的指针(它包含指向其父作用域的指针,依此类推).因此,在您的示例中,在for
循环执行完毕后,范围可能如下所示:
setupHelpScope = {
helpText:<...>,
i: 3,
item: {'id': 'age', 'help': 'Your age (you must be over 16)'},
parentScope: <...>
}
Run Code Online (Sandbox Code Playgroud)
您创建的每个闭包都将指向此单个范围对象.如果我们列出你创建的每个闭包,它看起来像这样:
[anonymousFunction1, setupHelpScope]
[anonymousFunction2, setupHelpScope]
[anonymousFunction3, setupHelpScope]
Run Code Online (Sandbox Code Playgroud)
当任何这些函数执行时,它使用传递的范围对象 - 在这种情况下,它是每个函数的相同范围对象!每个都将查看相同的item
变量并查看相同的值,这是您的for
循环设置的最后一个值.
要回答您的问题,无论您是var item
在for
循环上方还是在循环内部添加都无关紧要.因为for
循环不创建自己的作用域,所以item
将存储在当前函数的作用域字典中,即setupHelpScope
.for
循环内生成的外壳始终指向setupHelpScope
.
一些重要的说明:
for
循环没有自己的作用域 - 它们只是使用封闭函数的作用域.这也是真实的if
,while
,switch
,等等.如果这是C#,另一方面,将每个循环创造了一个新的范围对象,每个关闭将包含一个指向自己独特的范围.anonymousFunction1
在其范围内修改变量,则会为其他匿名函数修改该变量.这可能会导致一些非常奇怪的互动.进一步阅读:
归档时间: |
|
查看次数: |
7677 次 |
最近记录: |