为什么Javascript是这样工作的?

为什么Javascript是这样工作的?,javascript,functional-programming,Javascript,Functional Programming,所以我在推特上看到了某人的推特,提到下面的脚本是在一次求职面试中给出的 for (var i = 0; i < 3; i+=1) { setTimeout(function() { console.log(i) }, 100) } for(变量i=0;i

所以我在推特上看到了某人的推特,提到下面的脚本是在一次求职面试中给出的

for (var i = 0; i < 3; i+=1) {
    setTimeout(function() {
        console.log(i)
    }, 100)
}
for(变量i=0;i<3;i+=1){
setTimeout(函数(){
控制台日志(i)
}, 100)
}
被采访者被要求预测这个脚本的结果,他在推特中提到这对他来说是显而易见的

对于我来说,作为对Javascript完全陌生(或不感兴趣)的人,很难理解为什么结果既不是:

1) 依次为0、1、2。(作为传统的“for循环”)

2) 。只打印3次。(如果在调用函数之前完全完成了迭代)

但是打印3次


这是因为Javascript不是一种顺序编程语言,或者不是Javascript的特征,或者两者兼而有之?

要理解这一点,您需要了解Javascript是如何基于事件的,以及作用域是如何工作的,这就是为什么我假设它被用作面试问题的原因

基本上,JavaScript是基于事件队列的。在提供的示例代码中,
setTimeout
不会立即执行代码,但会让引擎知道代码应在100ms内触发

但是,的
循环会立即执行。如果使用块作用域变量定义,例如
let i=0
,这本身就不会是问题。例如,以下代码将打印0、1、2:

for(设i=0;i<3;i+=1){
setTimeout(函数(){
控制台日志(i)
}, 100)
}
但是,在代码中使用var
,它被“提升”到函数定义的顶部,或者在本例中是代码段/文件的顶部。因此,实际上,发动机的代码如下所示:

vari;
对于(i=0;i<3;i+=1){
setTimeout(函数(){
控制台日志(i)
}, 100)
}

这有一个有趣的效果:现在for循环运行三次,将
i
增加到值
3
。然后,在for循环完成执行后,调用
setTimeout
回调函数,该函数在其范围内具有相同的
i
值(因为它已被提升)。这就是为什么它会打印三次。

要理解这一点,您需要了解JavaScript是如何基于事件的,以及作用域是如何工作的,这就是为什么我假设它会被用作面试问题的原因

基本上,JavaScript是基于事件队列的。在提供的示例代码中,
setTimeout
不会立即执行代码,但会让引擎知道代码应在100ms内触发

但是,的
循环会立即执行。如果使用块作用域变量定义,例如
let i=0
,这本身就不会是问题。例如,以下代码将打印0、1、2:

for(设i=0;i<3;i+=1){
setTimeout(函数(){
控制台日志(i)
}, 100)
}
但是,在代码中使用var
,它被“提升”到函数定义的顶部,或者在本例中是代码段/文件的顶部。因此,实际上,发动机的代码如下所示:

vari;
对于(i=0;i<3;i+=1){
setTimeout(函数(){
控制台日志(i)
}, 100)
}

这有一个有趣的效果:现在for循环运行三次,将
i
增加到值
3
。然后,在for循环完成执行后,调用
setTimeout
回调函数,该函数在其范围内具有相同的
i
值(因为它已被提升)。这就是为什么它会打印3次。

这是因为当您使用关键字var声明变量时,您声明了全局(在本例中)变量。现在考虑<强> >作为执行的任务。strong>setTimeout添加另一个要执行的任务,但在for循环完成之后。所以执行顺序是:

  • 使用for循环迭代3次。并将i变量增加3倍
  • Logi变量3次,因为您调用了setTimeout3次
  • 如果你想了解更多,这里有一篇很棒的文章:


    但是当您使用let关键字声明i变量时,它是局部变量。它的范围是for循环的一次迭代。因此,对于每一次迭代,都有不同的变量(它不会被下一次迭代覆盖),

    这是因为当您使用关键字var声明变量时,您声明了全局(在本例中)变量。现在考虑<强> >作为执行的任务。strong>setTimeout添加另一个要执行的任务,但在for循环完成之后。所以执行顺序是:

  • 使用for循环迭代3次。并将i变量增加3倍
  • Logi变量3次,因为您调用了setTimeout3次
  • 如果你想了解更多,这里有一篇很棒的文章:


    但是当您使用let关键字声明i变量时,它是局部变量。它的范围是for循环的一次迭代。因此,对于每一次迭代,都有不同的变量(它不会被下一次迭代覆盖),

    当您使用
    var
    声明变量时,该变量的范围是定义它的函数(如果它在函数中定义)或全局变量(在这种情况下,它没有在函数中定义)。因此,js将记住
    i
    的值,直到代码激活

    循环将执行3次,并将变量
    i
    设置为3,且不再更改

    i
    的值已经设置为3时,
    setTimeout
    方法将在0.1秒后调用作为第一个参数传递的回调,因此它将在
    控制台中持续显示3。在回调执行期间,日志将作为
    i
    的值。