setTimeout 中 let 和 var 的区别? [复制]

Posted

技术标签:

【中文标题】setTimeout 中 let 和 var 的区别? [复制]【英文标题】:Difference between let and var inside setTimeout? [duplicate] 【发布时间】:2017-11-20 06:30:35 【问题描述】:

我知道 let 和 var 之间的区别。 let 是块作用域,var 是函数作用域。

for(var i=0; i< 3; i++)
    setTimeout(function()
        console.log(i);
    , 10);


output : 3
         3
         3

我知道上面的代码 sn-p 是如何工作的(console.log(i) 在 i 的值为 3 时执行,因为 i 的范围是全局的)。

但是

for(let i=0; i< 3; i++)
    setTimeout(function()
        console.log(i);
    , 10);


output : 1
         2
         3

上面的代码 sn-p 让我很困惑。根据我的说法,它应该抛出引用错误(因为console.log(i)执行的时间会在全局范围内而不是在本地范围内查看i的值,并且我没有在全局范围内声明/定义。所以它应该给出引用错误。)

谁能解释第二个 for 循环如何在运行时工作?

【问题讨论】:

在使用 let 时,闭包会在每次迭代中关闭 i 的新实例。 如果Reference Error 会被抛出,那么任何一种关闭都不会起作用。既不是函数范围var 的那个,也不是let 的那个。 有空的时候去看看这个article。它很好地解释了作用域、提升等。 【参考方案1】:

第二个示例(使用let)之所以有效,是因为函数在声明时将关闭作用域内的所有变量。 for 循环的每次迭代都会使用let 创建一个新变量,超时中的函数会关闭变量并保持引用。当函数在超时后被取消引用时,它的闭包变量也是如此。

有关函数闭包的更多信息,请参阅How do javascript closures work。

【讨论】:

【参考方案2】:

这就是关闭的魔力。在你的循环中

for(let i=0; i< 3; i++)
    setTimeout(function()
        console.log(i);
    , 10);

你在声明一个函数

function()
  console.log(i);

另外,循环本身声明了一个block

for(let i=0; i< 3; i++)
  // this is a block scope because it is contained in 
  // braces

let 定义的变量是块范围的。

由于closure,您在循环中声明的函数可以访问在其范围及其父范围中声明的所有变量,直到它被垃圾回收。

闭包是函数和词法环境的组合 在其中声明了该函数。这个环境包括 关闭时范围内的任何局部变量 已创建。

setTimeout 使用的函数被创建时,变量i 在范围内。引用的i 是循环的每次迭代的i 的不同实例。

函数一直存在,直到您声明的时间间隔过去。这就是为什么在循环中声明的 3 个函数中的每一个都打印出 i 的值; 它是在包含范围内声明的,并且对函数仍然可用

【讨论】:

【参考方案3】:

当您在此上下文中使用 let 时,每次迭代都会创建一个新的绑定/范围。如果你想在 ES5 中使用 var 实现类似的行为,你必须使用 IIFE:

for (var i = 0; i < 3; i++) 
  (function (i) 
    setTimeout(function () 
      console.log(i);
    , 10);
  )(i);

【讨论】:

以上是关于setTimeout 中 let 和 var 的区别? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

JS里的let与var(与君共勉)

javascript常见面试题

let和var,const的区别(非原创/自己整理成了适合自己看的样子)

Javascript中的var和let

let const var 区别

javascript中的let vs var [重复]