今天群里看到有人问那道经典的面试题:
for(let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i)
}, 300)
}
// 输出结果 0 1 2
下面试着解释一下,希望对你有帮助:
这里涉及到很多概念:
-
var
与let
的区别之一:var
声明会有变量提升,let
则没有,且let
会有块级作用域block scope
-
块级作用域到底是什么:通俗解释就是变量声明之后,能访问到该变量的区域(先忽略暂时性死区)。而能访问到的所有作用域(包括块级、函数级、全局)是因为词法作用域。
-
词法作用域到底是什么:是访问规则,即内部的作用域可以访问外部作用域的这样一个事实。比如你定义一个函数,该函数内部可以访问全局作用域中的变量或者函数。而如果你定义了一个函数,且这个函数与词法作用域的变量产生关联(访问该变量等)就会产生闭包。而闭包能让你在函数执行时仍然访问到该变量,可以理解成记住了该变量。(这就是为什么闭包可能产生内存泄露,因为变量一直被引用,无法被
Garbage Collection
) -
还有一个特别的点:
for
let
会产生多个块级作用域,至于为什么,标准里的: 262.ecma-international.org/6.0/#sec-cr… 。
由上述四点可以得出:setTimeout
的回调函数产生了闭包,回调函数一共执行三次,三次执行的函数所关联的是三个不同的块级作用域(闭包),而因为闭包记住的三个块级作用域中的i
的值分别是0
,1
,2
。 所以打印结果就是 0 1 2
。