closure是什么时候生成的,具体又是怎样工作的
学完 js ES6后有一些问题
我们都知道用var和let声明变量之间的区别,var声明会被提升到函数头部而let不会有任何提升,let是block级的scope而var是function或者global级的scope。 但是遇到了一个经典题目的时候产生了一些疑问
var arr = [1, 2, 3];
for (var i = 0; i < arr.length; i++) {
setTimeout(function () {
console.log(arr[i]);
}, 1000);
}
这段代码的输出是 三个undefined,如果希望他正常输出1,2, 3 需要使用let声明循环变量,官方给出的解释是var的scope是function, let声明变量的scope是block。但是当我深入探究他的原理的时候有一点没有想明白。 在循环体中每一次运行setTimeout函数会向计时器模块发送异步任务,该异步任务的回调函数是console.log(arr[i]), for循环结束之后主循环就结束了,所有变量被弹栈,但是由于回调函数需要使用到数组arr和变量i,所有arr和i被称为捕获变量,生成一个closure 存在堆内存中。
我的问题是
- 在循环体中,是否每一次调用setTimeout都会为匿名函数生成一个closure,如果是的话,是否是立即生成,如果是立即生成,那此时closure里面的i值应该是正确的, 从结果来看并不是这样
- 在i=0时候调用setTimeout, 立即为匿名函数生成了一个closure,在之后的循环中匿名函数也指向这个closure,但是 本地栈内存也可以修改这个closure, 导致最后调用console的时候取i值是变化后的i
- 在调用setTimeout的时候不会生成closure, 之后当console log被调用的时候才会生成closure, 如果这样的话那么毫无疑问这个closure里的i值就一定是3. 4.本地函数在被调用的时候就同步生成一个closure,如果子函数被补货了就在堆内存中保留这个closure,如果没有被补货就删除这个closure, 所以每一次循环体内的cons ole都指向这个被修改过的closure。 我不清楚具体的流程是怎么样的,希望有大神可以回答一下。