duyue6002 / Blog

:pencil2: Write here
http://duyue6002.github.io/Blog/#/
5 stars 1 forks source link

[总结]循环中的闭包 #11

Open duyue6002 opened 5 years ago

duyue6002 commented 5 years ago

由循环中的闭包引发的血案

for (var i = 1; i < 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i*1000);
}

运行时,以每秒一次的频率输出5,与输出期望值(1 2 3 4)不同。

原因

延迟函数的回调发生在循环结束时才执行,即使setTimeout(.., 0)结果也相同。循环不会等待回调 实际上,代码可以被看作:

var i;
for (i = 1; i < 5; i++) {
  setTimeout(function timer() {
    console.log(i);
  }, i*1000);
}

标识符i属于全局变量,尽管每个循环都定义了延迟函数,但它们都被封闭在一个全局作用域中,共享同一个i的引用。

在这段代码中,需要更多的闭包作用域,每次迭代都需要一个作用域。

解决

块作用域拥有自己的变量,用来存储i的值

使用立即执行函数

for (var i = 1; i < 5; i++) {
(function(j) {
setTimeout(function timer() {
console.log(j);
}, j*1000);
)(i);
}

使用ES6

for (let i = 1; i < 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i*1000);
}

let循环在每次迭代时,都会进行重新绑定的行为,let声明附属于新的作用域而不是当前的作用域代码相当于:

let i;
for (i = 1; i < 5; i++) {
let j = i;
setTimeout(function timer() {
console.log(j);
}, j*1000);
}

ES6大法好!!