我正在参与掘金新人创作活动,一起开启写作之路。
关键词:作用域,闭包,延时函数
以下代码会打印5次5,而不会依次打印0~4:
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i)
}, 1000)
}
原因:通过var声明的变量会发生变量提升。即等价于以下代码:
var i; // i为全局变量
for (i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i)
}, 1000)
}
// 或者下面这样展示更直观
var i; // i为全局变量
for (i = 0; i < 5; i++) { }
// i==5了
setTimeout(() => {
console.log(i) // 5
}, 1000)
setTimeout(() => {
console.log(i) // 5
}, 1000)
setTimeout(() => {
console.log(i) // 5
}, 1000)
setTimeout(() => {
console.log(i) // 5
}, 1000)
setTimeout(() => {
console.log(i) // 5
}, 1000)
静态代码执行完时(i=5时跳出循环),全局变量i就已经变为5了。1s之后,由于setTimeout内的变量i来自全局作用域,因此执行setTimeout时,传入的是全局变量i的最终值:5。效果就是打印5次5。
解决方法:
方法1:给setTimeout封装到一个局部函数,每个局部函数都拥有一个独立的作用域,内部的变量会遮蔽全局作用域的同名变量,也不会受其他代码块的影响,俗称闭包。
for (var i = 0; i < 5; i++) {
(
// 形参m由i赋值
function (m) {
setTimeout(() => {
console.log(m)
}, 1000);
}
)(i)
}
方法2:使用let或const关键词
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i)
}, 1000)
}
方式3:块级作用域(和方法2一样)
for (var i = 0; i < 5; i++) {
{
// {}内添加一个let或const后,会成为块作用域
let j = i;
setTimeout(() => {
console.log(j);
}, 1000);
}
}
以上内容含自己的理解,仅用于自学记录。