它没有按预期工作。无论你关注什么领域, 将显示有关您年龄的消息。
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();
确实提供了解决方案。这是另一个封闭。
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function makeHelpCallback(help) {
return function() {
showHelp(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 = makeHelpCallback(item.help);
}
}
setupHelp();
MDN给出的解释是因为第二种解决方案创造了3种环境。
任何人都可以给我解释一下,“环境”的含义是什么,我该如何判断其范围?
答案 0 :(得分:3)
在函数中使用变量名时,首先JavaScript解释器将该变量检查为函数中的局部变量。如果不存在这样的局部变量,则它会遍历到外部范围以查找变量。 (当定义一个函数时,它可以访问作用域链 - 作用域的层次结构,从定义它的外部函数的作用域开始,然后继续执行包含 函数的函数等等。)
这里,匿名onfocus
处理函数使用变量标识符item
。 item
不是函数中的局部变量,因此当执行处理程序时,JavaScript必须遍历下一个外部作用域,它在item
中定义setupHelp
。您为每个项目定义了一个处理函数;但是,所有这些处理程序函数都在外部作用域中保存了对item
的相同的引用。每当调用这些函数中的任何一个时,JavaScript都会查找item
的相同值,这是for
循环完成时所持有的值。
在具有额外闭包的情况下,将item.help
的当前值作为参数传递给makeHelpCallback
,后者返回处理函数。由于JavaScript按值传递变量,因此每个处理函数现在都有一个单独的外部作用域,其值为help
。
答案 1 :(得分:2)
考虑一个更简单的例子:
for (var i = 0; i < 10; ++i) {
document.getElementById('elem' + i).addEventListener('click', function() {
document.getElementById('elem' + i).style.display = 'none';
});
}
代码看起来很简单,对吧?看起来如果你单击一个它将隐藏它。
但事情并非如此。事件处理程序在i
左右关闭。无论处理程序之外的i
发生了什么,都会在处理程序内发生i
(为了简化一些事情)。
Here就是这种行为的一个例子。请注意,无论您点击什么div,ELEM #10
始终是隐藏的。 (它是10而不是9,因为i
在循环条件失败之前最后一次递增。)
诱人的解决方法是试试这个:
for (var i = 0; i < 10; ++i) {
document.getElementById('elem' + i).addEventListener('click', function() {
var fix = i;
document.getElementById('elem' + fix).style.display = 'none';
});
}
这不起作用,因为在i
已经10
之前不会处理分配。 (好吧,从技术上来说,直到点击事件发生才进行处理 - 假设i
是10,然而理论上,点击可能在所有事件被绑定之前发生,使i
低于当时10。)
你实际上可以使用内联使用的MDN技巧:
for (var i = 0; i < 10; ++i) {
document.getElementById('elem' + i).addEventListener('click', (function(j) { return function() {
document.getElementById('elem' + j).style.display = 'none';
}}(i)));
}
将变量传递给函数会使其被视为不同的“环境”。函数内部的j
在调用函数时被冻结。然后内部闭合使用j。有趣的是,i
仍然存在于这两个函数中。根据访问时间,它会介于0
和10
之间。
for (var i = 0; i < 10; ++i) {
document.getElementById('elem' + i).addEventListener('click', (function(j) {
alert(i); //whichever value is being bound
//note that i === j is always true here
//j is a copy of i though and thus stops changing when i changes in the future
return function() {
alert(i); //always 10
document.getElementById('elem' + j).style.display = 'none';
};
}(i)));
}
思考JS作用域的简化方法是嵌套函数继承父作用域(这是真的)。调用函数时,变量将按原样传递给调用范围。
这里有一个有趣的小警告:基元通过值和对象通过引用传递。
var x = 5; (function(n) { ++n; }(x)); /* x is still 5 */
var o = {foo: 'bar'}; (function(obj) { obj.far = 'baz'; }(o)); /* o === {foo: 'bar', far: 'baz'} */