shfshanyue / Daily-Question

互联网大厂内推及大厂面经整理,并且每天一道面试题推送。每天五分钟,半年大厂中
https://q.shanyue.tech
4.92k stars 510 forks source link

【Q566】关于块级作用域,以下代码输出多少,在何时间输出 #581

Open shfshanyue opened 3 years ago

shfshanyue commented 3 years ago
for (var i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 1000)
}
shfshanyue commented 3 years ago

一秒之后连续输出五个5,以下可输出预期效果

for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 1000 * i)
}

for (var i = 0; i < 5; i++) {
  setTimeout(console.log, 1000 * i, i)
}
aotushi commented 3 years ago

一秒之后连续输出五个5,以下可输出预期效果

for (let i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 1000 * i)
}

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

为什么setTimeout格式不同,输出的结果也不同.例如:当for循环采用var声明变量,但setTimeout(console.log,1000*i,i)与setTimeout(()=>console.log(i),0)输出的结果不一样.

是因为函数作用域?

hwb2017 commented 2 years ago

第一种使用var的方式:

for (var i = 0; i < 5; i++) {
  setTimeout(() => console.log(i), 1000)
}

第二种使用var的方式:

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

var 声明的变量是在函数作用域或者全局作用域的,在第一种方式中,由于setTimeout是异步执行,且它是从闭包中获取 i 变量,由于 i 是在函数/全局作用域中声明的,所以5次循环中 i 不断被赋值,最后 i 的值为5,执行的结果为连续的5个5。

在第二种方式中,通过给setTimeout的回调函数传参的方式,保存了每次循环中 i 的值,因此执行结果符合预期

let声明的变量是在块级作用域(花括号)中的,因此可以认为每次执行循环语句块中的 i 变量是互相独立的,所以执行结果也符合预期