在 for 循环中初始化的变量的作用域规则
Scoping rules for variables initialized in a for loop
可能的重复项:
循环内的Javascript闭包 - 简单的实际示例
我正在我的一个项目中使用 setTimeout,以限制向 DOM 添加元素(因此 UI 在页面加载期间不会冻结)。但是,我遇到了一些让我有点困惑的事情。给定此代码:
for(var i = 0; i < 5; i++) {
var j = i + 10;
console.log("i is: " + i + " j is: " + j);
setTimeout(function() {
console.log("in timeout i is: " + i + " j is: " + j);
}, i * 1000);
}
我得到以下输出:
i is: 0 j is: 10
i is: 1 j is: 11
i is: 2 j is: 12
i is: 3 j is: 13
i is: 4 j is: 14
in timeout i is: 5 j is: 14
in timeout i is: 5 j is: 14
in timeout i is: 5 j is: 14
in timeout i is: 5 j is: 14
in timeout i is: 5 j is: 14
超时中 i
的值为 5 是显而易见的,因为 i 的作用域为 for 循环初始化。但是,为什么所有超时输出j
14 是?我本以为j
会在超时输出 10、11、12、13、14,因为它的作用域在循环内。我怎样才能达到这个结果?
这是因为,在JavaScript中,var
具有函数作用域。
var
声明将被提升到当前执行上下文的顶部。也就是说,如果它位于函数内部,则var
将是函数执行上下文中的范围,否则是程序(全局)执行上下文。
ECMAScript 2015(又名 ES6)引入了let
,它允许您创建块作用域变量,但由于它没有得到广泛支持,我只留下链接以供参考。
要仍然使用 var
并将其"作用域"在循环中,一种解决方法是创建新的执行上下文,也称为闭包:
function callbackFactory(i, j) {
// Now `i` and `j` are scoped inside each `callbackFactory` execution context.
return function() { // This returned function will be used by the `setTimeout`.
// Lexical scope (scope chain) will seek up the closest `i` and `j` in parent
// scopes, that being of the `callbackFactory`'s scope in which this returned
// function has been initialized.
console.log("in timeout i is: " + i + " j is: " + j);
};
}
for(var i = 0; i < 5; i++) {
var j = i + 10;
console.log("i is: " + i + " j is: " + j);
setTimeout( callbackFactory(i, j), i * 1000);
}
由于我在回调作用域内限定了i
和j
的范围,因此它们将在setTimeout
内返回与传递给callbackFactory
时相同的值。
请参阅现场演示。
执行相同操作的另一种方法是在for
循环中创建 IIFE。这通常更容易阅读,但 JS(H|L)int会对你大喊大叫。 ;)
这是因为在循环中创建函数被认为对性能不利。
for(var i = 0; i < 5; i++) {
var j = i + 10;
console.log("i is: " + i + " j is: " + j);
(function(i, j) { // new execution context created for each iteration
setTimeout(function() {
console.log("in timeout i is: " + i + " j is: " + j);
}, i * 1000);
}(i, j)); // the variables inside the `for` are passed to the IIFE
}
上面我在每次迭代的for
内创建了一个新的执行上下文。(演示)
将第一种方法(callbackFactory
)与上面的IIFE混合在一起,我们甚至可以做出第三种选择:
for(var i = 0; i < 5; i++) {
var j = i + 10;
console.log("i is: " + i + " j is: " + j);
setTimeout(function(i, j) {
return function() {
console.log("in timeout i is: " + i + " j is: " + j);
};
}(i, j), i * 1000);
}
这只是使用 IIFE 代替callbackFactory
函数。这似乎不太容易阅读,并且仍然在for
循环中创建对性能不利的函数,但只是注意到这也是可能的并且有效。
这3种方法在野外很常见。 =]
哦,差点忘了回答主要问题。只需将callbackFactory
放在与for
循环相同的作用域中,然后不要在其中限定i
的范围,而是让作用域链查找外部作用域的i
:
(function() {
var i, j;
function callbackFactory(j) {
// the `j` inside this execution context enters it as a formal parameter,
// shadowing the outer `j`. That is, it is independent from the outer `j`.
// You could name the parameter as "k" and use "k" when logging, for example.
return function() {
// Scope chain will seek the closest `j` in parent scopes, that being
// the one from the callbackFactory's scope in which this returned
// function has been initialized.
// It will also seek up the "closest" `i`,
// which is scoped inside the outer wrapper IIFE.
console.log("in timeout i is: " + i + " j is: " + j);
};
}
for(i = 0; i < 5; i++) {
j = i + 10;
console.log("i is: " + i + " j is: " + j);
setTimeout( callbackFactory(j), i * 1000);
}
}());
/* Yields:
i is: 0 j is: 10
i is: 1 j is: 11
i is: 2 j is: 12
i is: 3 j is: 13
i is: 4 j is: 14
in timeout i is: 5 j is: 10
in timeout i is: 5 j is: 11
in timeout i is: 5 j is: 12
in timeout i is: 5 j is: 13
in timeout i is: 5 j is: 14 */
小提琴
请注意,我已将i
和j
声明移至范围的顶部,只是为了提高可读性。它与for (var i = [...]
具有相同的效果,后者将由解释器吊起。
- 将函数的上下文应用于javascript变量
- 无法导出函数expressjs/requestjs中的变量
- 函数参数中的数据与指定变量之间的任何性能差异
- 将PHP变量传递给jQuery时遇到问题
- 如何通过ajax刷新JSF填充的javascript变量
- 参数变量出现ngTable指令问题
- 通过javascript重定向html传递php变量
- 将jsp文件下拉列表中的选定项分配给一个java变量(比如String selection)
- 全局变量和全局对象的属性之间有什么区别吗
- 如何在Bootstrap Modal中为动态点击生成的变量设置jade属性
- .validate规则:检查jquery变量
- 如何在客户端验证中验证两个变量规则
- 捕获直接调用规则变量的正确方法是什么
- ESLint的“no-undef”规则将我对Underscore的使用称为未定义的变量
- 如何使用纯JavaScript在内联CSS规则中插入变量
- 禁止特定变量/方法的esint规则
- 在JS中,一个变量在一个循环中引用相同的数据有什么规则?
- 我可以创建一个ESLint规则,允许所有的全局变量匹配一个正则表达式吗?
- 在 for 循环中初始化的变量的作用域规则
- 将CSS规则放入javascript变量中