循环和闭包
2021.03.18 Thu
1 |
|
如果代码的初衷每隔一秒地打印1 2 3 4 5,上面的代码却只能每隔一秒地打印6 6 6 6 6。
因为 setTimeout 是异步事件,当 js 遇到异步事件时会把异步事件放到执行队列里,于是每一次 for 循环遇到的 setTimeout 都被放到了执行队列等待执行,但是因为 for 循环的阻塞机制,在执行队列里的所有 setTimeout 将不得不等到 for 循环结束才能回到主线程里。
而等到 for 循环结束,i 的值已经变成了 6.尽管每次循环都定义了一个 setTimeout,但是它们都被 for 循环封闭在了一个共享的全局作用域中,它们实际上摸到的 i 值其实都是一个 i,所以打印出来的都是 6.
将代码改写
1 |
|
这个时候代码终于如愿以偿地实现了人类无聊的初衷,每隔一秒地打印1 2 3 4 5
上面把每一次 for 循环的执行操作写成了立即执行函数,在循环过程中每个迭代都生成了一个闭包作用域,于是 setTimeout 的每一次回调都可以看成一个闭包,它可以一直访问它所在作用域里的变量。
随着时代的发展,代码可以是这个样子
1 |
|
let 可以用来劫持块作用域并且在这个块作用域中声明一个变量。
for 循环头部的 let 声明还会有一个特殊的行为,这个行为指出变量在循环过程中不止被声明一次,每次迭代都会声明。随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。