Javascript 在for循环中使用函数时范围问题的低级解释

Javascript 在for循环中使用函数时范围问题的低级解释,javascript,Javascript,昨晚我狼吞虎咽地读完了这本书 在中,关于作用域闭包,它给出了javascript中作用域机制如何导致for循环以不可预测的方式运行的示例 根据文本,以下for循环将数字6显示五次 for(var i=1; i<=5; i++) { setTimeout( function timer(){ console.log( i ); }, i*1000 ); } 我可以接受这一点,并记住要经常观察这一特点,但我更愿意理解为什么 请有人在编译器、引擎和作用域的上下文中详细解释为什么第一个循环不工作

昨晚我狼吞虎咽地读完了这本书

在中,关于作用域闭包,它给出了javascript中作用域机制如何导致
for
循环以不可预测的方式运行的示例

根据文本,以下for循环将数字
6
显示五次

for(var i=1; i<=5; i++) { setTimeout( function timer(){ console.log( i ); }, i*1000 ); }
我可以接受这一点,并记住要经常观察这一特点,但我更愿意理解为什么


请有人在编译器、引擎和作用域的上下文中详细解释为什么第一个循环不工作?

for循环中的i是在全局作用域中创建的,即只有一个i递增。因此,在超时之后,全局i已经增加了5倍,值为6。然而,由于每个j都是在闭包内单独创建的,因此存在5个单独的j局部变量-每个变量的作用域仅限于闭包的上下文,每个变量都分配了一个递增值i。希望这能解释:)

for循环中的i是在全局范围内创建的,即只有一个i递增。因此,在超时之后,全局i已经增加了5倍,值为6。然而,由于每个j都是在闭包内单独创建的,因此存在5个单独的j局部变量-每个变量的作用域仅限于闭包的上下文,每个变量都分配了一个递增值i。希望这能解释:)

问题是,您在
for
循环中创建的闭包函数记住了对变量
i
的引用。
setTimeout
使函数调用异步(因此它在循环之后运行,不管您在循环中输入了什么延迟值),并且
i++
在每次迭代中更改
i
的值。当闭包运行时,所有闭包的值都相同,因为它们都引用了相同的变量。在第二个示例中,您创建并运行一个匿名函数,该函数创建自己的作用域,并通过执行
var j=i
在匿名函数的作用域中创建一个变量
j
,该变量包含此时
i
值的副本。在
setTimeout
中运行的函数会记住从创建变量的作用域中对变量
j
的引用,因此它具有您期望的值

就我个人而言,我会将
I
作为参数传递给这个匿名函数,因为我发现它比上面显示的示例更干净:

for (var i = 0; i < 5; ++i) {
    (function (j) {
        setTimeout(function timer() {
            console.log(j);
        }, j * 1000);
    }(i))
}
for(变量i=0;i<5;++i){
(职能(j){
setTimeout(函数计时器(){
控制台日志(j);
},j*1000);
}(i) )
}

现在,您可以使用,但目前只能在新的浏览器和节点中使用;关于向后兼容性,请看一看。

问题是,您在
for
循环中创建的闭包函数记住了对变量
i
的引用。
setTimeout
使函数调用异步(因此它在循环之后运行,不管您在循环中输入了什么延迟值),并且
i++
在每次迭代中更改
i
的值。当闭包运行时,所有闭包的值都相同,因为它们都引用了相同的变量。在第二个示例中,您创建并运行一个匿名函数,该函数创建自己的作用域,并通过执行
var j=i
在匿名函数的作用域中创建一个变量
j
,该变量包含此时
i
值的副本。在
setTimeout
中运行的函数会记住从创建变量的作用域中对变量
j
的引用,因此它具有您期望的值

就我个人而言,我会将
I
作为参数传递给这个匿名函数,因为我发现它比上面显示的示例更干净:

for (var i = 0; i < 5; ++i) {
    (function (j) {
        setTimeout(function timer() {
            console.log(j);
        }, j * 1000);
    }(i))
}
for(变量i=0;i<5;++i){
(职能(j){
setTimeout(函数计时器(){
控制台日志(j);
},j*1000);
}(i) )
}

现在,您可以使用,但目前只能在新的浏览器和节点中使用;关于向后兼容性,请查看。

这必须是重复的…这必须是重复的…好答案。有一件事仍然不清楚:您不是说过函数在for循环之后运行吗?如果变量引用出现在函数中,它们不会也设置在for循环之后,因此具有相同的值吗?
var=
如何使它并发运行,即使它位于假定在for循环之后运行的函数内部?这是一个引擎与编译器的问题吗?为什么呢“我遗漏了什么吗?”卢帕图斯,我对你的回答补充了一点解释,并重新表述了一些内容——希望你不介意。如果你不喜欢这些改变,我很乐意写一个单独的答案,并回复这个;我只是觉得你说了我将要说的话。@AndÚ像
(function(){…}())
这样的函数运行到位,我说如果你把函数放在
setTimeout
中,它会在-setTimeout之后运行,将传递给它的函数放到下一个运行循环中(或者稍后,如果有延迟),所以在所有同步代码之后。@andyg0808谢谢,我只是从关于setTimeout的部分中删除了
大部分
,因为它不是大部分-setTimeout将东西放在运行队列的末尾,所以它总是在同步代码之后运行(如果我错了,请纠正我,可能是我的英语有问题;)。ÚJS中没有真正的并发性(我的意思是事情从来都不是平行的),所有东西都在单线程中运行,但是
setTimeout
put函数在其他线程之后传递给它。请参阅关于它如何工作的文章。回答得好。有一点仍然不清楚:您不是说函数在for循环之后运行吗?如果变量引用出现在函数中,它们不会也在for循环之后设置吗我们有