由一个简单的问题引发的一连串的思考

246 阅读2分钟

最近在学习 JS 函数,我发现一个特别有意思的问题:

let i = 0
for(i = 0; i<6; i++){
console.log(i)
}

以上的这一段代码打印出的结果是 0 1 2 3 4 5,我想大家应该都不会有任何疑问。

但是,加了一个定时器之后...

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

它打印的结果竟然变成了6个6,为此,我特地看了一下mdn文档,发现定时器的作用是把一段代码延后执行,哪怕设置的是0毫秒以后,那它也会等待其他 js 代码执行完才执行。

因此,这就很好理解了,for 循环执行完之后 i 的值为 6 ,上面一段代码是等for 循环执行完之后再打印 6 个 i ,那么结果是 6 个 6 便不足为奇了。

既然理解了前因后果,那有没有方法让它输出0 1 2 3 4 5呢?

答案是有的,以下这段代码打印的结果便是0 1 2 3 4 5。

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

看了这段代码,我想大家可能会有跟我一样的疑问,为什么在for循环外面 let i = 0,结果是6个6,在里面 let i = 0 ,结果就会是0 1 2 3 4 5呢?

于是,我猜想,我们可以把上面的for循环看成以下一段代码

{
  let i = 0
  setTimeout(()=>{
    console.log(i)
  },0)
}
{
  let i = 1
  setTimeout(()=>{
    console.log(i)
  },0)
}
{
  let i = 2
  setTimeout(()=>{
    console.log(i)
  },0)
}
{
  let i = 3
  setTimeout(()=>{
    console.log(i)
  },0)
}
{
  let i = 4
  setTimeout(()=>{
    console.log(i)
  },0)
}
{
  let i = 5
  setTimeout(()=>{
    console.log(i)
  },0)
}

由于 let 是只作用于当前块作用域内的,一目了然,打印结果就是 0 1 2 3 4 5。

如果以上思路是正确的,那么,我把 let 改成 const 是否可行呢?

for(const i = 0; i<6; i++){
  setTimeout(()=>{
    console.log(i)
  },0)
}

很遗憾,它给我报了一个错误Error: Assignment to constant variable.> 0,你以为我这样就失败了吗?

请耐心继续往下看

在机缘巧合之下,我看到了知乎大V方大大写的一篇文章:我用了两个月的时间才理解 let

于是,我再大胆猜想

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

我可以把以上的这一段代码看成下面的一段代码

 let i = 0
 if(i<6){
  let xxx = i
  setTimeout(()=>{
    console.log(xxx)
  },0)
  i++
}

这就能很好理解为什么只能用 let 而不能用 const 了

如果你此时对我的猜想还有疑问,那么请试试下面两段代码

let i = 0
for(i = 0; i<6; i++){
  let xxx = i
  setTimeout(()=>{
    console.log(xxx)
  },0)
}
let i = 0
for(i = 0; i<6; i++){
  const xxx = i
  setTimeout(()=>{
    console.log(xxx)
  },0)
}

它们最后输出的结果都是 0 1 2 3 4 5.