我试图通过学习如何使用闭包来扩展我适度的JavaScript技能水平。在下面的代码中,我想我会看到控制台.log输出从 3 倒计时到 0。相反,我得到-1,-1,-1,-1。
我知道我正在处理范围界定问题,但仅此而已。缺少什么?这应该如何正确编写,为什么?
function closure_count_test (number)
{
for (var x = 0; x <= number; x += 1)
{
setTimeout(function() {console.log(number - x);}, x * 1000);
}
}
closure_count_test(3);
你的逻辑是正确的。问题是 setTimout 只考虑变量的最新值。所以 setTimout 总是得到 x = 0,因为它是循环中的最后一个。
如果删除 setTimout 函数,您可以看到所需的输出。
x
由for
循环迭代,但setTimeout
中的函数使用变量x
,该变量在创建函数时不进行插值。 这导致它使用 x
的最终值(因为setTimeout
在循环完成后执行)。
为了解决这个问题,您必须将 x 的值原封不动地传递给 setTimeout
的回调。 您可以调用返回另一个函数(新回调)的函数:
for (var x = 0; x <= number; x += 1) {
setTimeout(
(function (x) {
return function () { console.log(number - x) };
})(x)
, x * 1000);
}
这会将x
从具有当前值的外部作用域传递到内部作用域。 内部函数使用创建函数时x
的任何值。
返回一个函数以在 setTimeout
中正常工作。
http://jsfiddle.net/ExplosionPIlls/QhA3a/
作用域的工作原理是,它使嵌套函数中引用的主函数变量在函数结束后保留,并且所有这些函数以后都可以访问它们。在提供的例子中,x 是在 main 函数中定义的变量,所有嵌套函数稍后都可以引用它。到那时,x 的值将是数字+1,所以你的结果非常有意义。要解决此问题,您必须避免引用 main 函数的变量。这是正常的技术:
function closure_count_test (number)
{
for (var x = 0; x <= number; x += 1)
{
setTimeout(function(x) {
return function() {console.log(number - x);}
} (x), x * 1000);
}
}
你在这里所做的是调用几个嵌套函数,这些函数有自己的x复制为参数,每个函数都有一个嵌套函数,该函数将通过作用域引用该参数。
有效的,因为 x 要定位在另一个闭包中
function closure_count_test(number) {
for (var x = 0; x <= number; x++) ( // not need {} as here is only one operator
function (x) { //Now x - local variable in anonymous function
return setTimeout(function () {
console.log(number - x);
}, x * 1000);
}(x) // pass x to anonymous function as argument
);
}
closure_count_test(3);