javascript for循环和超时函数
javascript for-loop and timeout function
我遇到了 for-loop 运行 setTimeout 的问题。
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
我期待输出
0
1
3
4
然而,出于某种原因,它们都输出 5。
变量x
定义在for循环的局部范围内,所以我认为这可能不计入setTimeout的回调。我测试了在 for 循环之外定义 x
。
var x = 10
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
我认为这个输出会给出 10,但它没有。然后我认为之后定义 x 是有意义的。
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
var x = 10
这 return 只有 10 个。这意味着回调都在 for 循环执行后调用?为什么在执行 for 循环后初始化变量后,它们只符合 for 循环的父范围?我错过了什么吗?
我知道如何让这个例子与
一起工作
for (var x = 0; x < 5; x++) {
var timeoutFunction = function(x) {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(x), 1)
}
然而,我真的很想知道缺少什么...
您必须为您的函数创建新范围,使其成为给定迭代的 'remember' 值:
for (var x = 0; x < 5; x++) {
var timeoutFunction = (function(x) {
return function() {
console.log(x)
}
})(x)
setTimeout(timeoutFunction(), 1)
}
另一个解决方案是使用 ES2015 let
:
for (let x = 0; x < 5; x++) {
var timeoutFunction = function() {
console.log(x)
}
setTimeout(timeoutFunction(), 1)
}
使用这个片段
for(let x = 0; x < 5; x++) {
var timeoutFunction = function() {
console.log(x);
};
setTimeout(timeoutFunction, 1);
};
console.log('For loop completed');
JavaScript 不是多线程的,所以对于你的代码,timeoutFunction 将在 for 循环完成后执行,因为你使用的是全局变量(相对于 timeoutFunction 上下文)最终结果被打印出来
请注意,将1
指定为延迟值实际上不会导致函数在1 毫秒后执行。该函数可以执行的最快速度大约为 9 毫秒(这是基于浏览器的内部结构),但实际上,在没有其他代码 运行 之前,它无法 运行 该函数。
至于意外输出,您遇到此行为是因为 timeoutFunction
包含对在更高范围内声明的 x
变量的引用。这会导致围绕 x
变量创建 closure,这样每次函数 运行s 它都不会获得 x
本身,但它共享 x
值,因为它是在更高范围内声明的。这是闭包的典型副作用。
有几种方法可以调整语法来解决问题...
复制 x
并通过将 x
传递给函数让每个函数使用自己的副本。当您传递基本类型(布尔值、数字、字符串)时,会创建数据的副本,这就是传递的内容。这打破了对共享 x
作用域的依赖。你的最后一个例子是这样做的:
for (var x = 0; x < 5; x++) {
var timeoutFunction = function(x) {
return function() {
console.log(x)
}
}
// Because you are passing x into the function, a copy of x will be made for the
// function that is returned, that copy will be independent of x and a copy will
// be made upon each loop iteration.
setTimeout(timeoutFunction(x), 1)
}
如果您的超时函数没有返回另一个函数(因为没有函数可以将值传递给),则需要另一个执行相同操作的示例。所以,这个例子创建了一个额外的函数:
for (var x = 0; x < 5; x++) {
// This time there is no nested function that will be returned,
function timeoutFunction(i) {
console.log(i);
}
// If we create an "Immediately Invoked Funtion Expression" (IIFE),
// we can have it pass a copy of x into the function it invokes, thus
// creating a copy that will be in a different scope than x.
(function(i){
setTimeout(function(){
timeoutFunction(i); // i is now a copy of x
}, 1);
}(x));
}
如果您使用支持 ECMAScript 2015 标准的浏览器,您只需将循环中的 var x
声明更改为 let x
,因此x
在循环的每次迭代中获得块级范围:
// Declaring a variable with let causes the variable to have "block-level"
// scope. In this case the block is the loop's contents and for each iteration of the
// loop. Upon each iteration, a new "x" will be created, so a different scope from
// the old "x" is what's used.
for (let x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
我遇到了 for-loop 运行 setTimeout 的问题。
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
我期待输出
0
1
3
4
然而,出于某种原因,它们都输出 5。
变量x
定义在for循环的局部范围内,所以我认为这可能不计入setTimeout的回调。我测试了在 for 循环之外定义 x
。
var x = 10
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
我认为这个输出会给出 10,但它没有。然后我认为之后定义 x 是有意义的。
for (var x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}
var x = 10
这 return 只有 10 个。这意味着回调都在 for 循环执行后调用?为什么在执行 for 循环后初始化变量后,它们只符合 for 循环的父范围?我错过了什么吗?
我知道如何让这个例子与
一起工作for (var x = 0; x < 5; x++) {
var timeoutFunction = function(x) {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(x), 1)
}
然而,我真的很想知道缺少什么...
您必须为您的函数创建新范围,使其成为给定迭代的 'remember' 值:
for (var x = 0; x < 5; x++) {
var timeoutFunction = (function(x) {
return function() {
console.log(x)
}
})(x)
setTimeout(timeoutFunction(), 1)
}
另一个解决方案是使用 ES2015 let
:
for (let x = 0; x < 5; x++) {
var timeoutFunction = function() {
console.log(x)
}
setTimeout(timeoutFunction(), 1)
}
使用这个片段
for(let x = 0; x < 5; x++) {
var timeoutFunction = function() {
console.log(x);
};
setTimeout(timeoutFunction, 1);
};
console.log('For loop completed');
JavaScript 不是多线程的,所以对于你的代码,timeoutFunction 将在 for 循环完成后执行,因为你使用的是全局变量(相对于 timeoutFunction 上下文)最终结果被打印出来
请注意,将1
指定为延迟值实际上不会导致函数在1 毫秒后执行。该函数可以执行的最快速度大约为 9 毫秒(这是基于浏览器的内部结构),但实际上,在没有其他代码 运行 之前,它无法 运行 该函数。
至于意外输出,您遇到此行为是因为 timeoutFunction
包含对在更高范围内声明的 x
变量的引用。这会导致围绕 x
变量创建 closure,这样每次函数 运行s 它都不会获得 x
本身,但它共享 x
值,因为它是在更高范围内声明的。这是闭包的典型副作用。
有几种方法可以调整语法来解决问题...
复制 x
并通过将 x
传递给函数让每个函数使用自己的副本。当您传递基本类型(布尔值、数字、字符串)时,会创建数据的副本,这就是传递的内容。这打破了对共享 x
作用域的依赖。你的最后一个例子是这样做的:
for (var x = 0; x < 5; x++) {
var timeoutFunction = function(x) {
return function() {
console.log(x)
}
}
// Because you are passing x into the function, a copy of x will be made for the
// function that is returned, that copy will be independent of x and a copy will
// be made upon each loop iteration.
setTimeout(timeoutFunction(x), 1)
}
如果您的超时函数没有返回另一个函数(因为没有函数可以将值传递给),则需要另一个执行相同操作的示例。所以,这个例子创建了一个额外的函数:
for (var x = 0; x < 5; x++) {
// This time there is no nested function that will be returned,
function timeoutFunction(i) {
console.log(i);
}
// If we create an "Immediately Invoked Funtion Expression" (IIFE),
// we can have it pass a copy of x into the function it invokes, thus
// creating a copy that will be in a different scope than x.
(function(i){
setTimeout(function(){
timeoutFunction(i); // i is now a copy of x
}, 1);
}(x));
}
如果您使用支持 ECMAScript 2015 标准的浏览器,您只需将循环中的 var x
声明更改为 let x
,因此x
在循环的每次迭代中获得块级范围:
// Declaring a variable with let causes the variable to have "block-level"
// scope. In this case the block is the loop's contents and for each iteration of the
// loop. Upon each iteration, a new "x" will be created, so a different scope from
// the old "x" is what's used.
for (let x = 0; x < 5; x++) {
var timeoutFunction = function() {
return function() {
console.log(x)
}
}
setTimeout(timeoutFunction(), 1)
}