var和let都是用来声明变量的。var声明的变量具有函数作用域,且具有变量提升和可重复命名;let声明的变量具有块级作用域,不能实现变量提升也不能重复命名,否则会报错。
@
TOC
一、for循环中的var
因为var具有函数作用域,因此在for循环内定义的迭代变量会渗透到for循环外面。
for(var i = 0;i < 5;++ i){
setTimeout(()=>console.log(i),0);
输出的结果是 5,5,5,5,5
for循环是同步任务,setTimeout是异步任务中的宏任务,根据js的事件循环机制,js会执行完所有的同步任务再去执行异步任务。当5次循环结束后,i的值为5,i渗透到for循环外部,这时候同步任务都执行完毕了,开始执行定时器宏任务,因此输出5次均为5。
二、for循环中的let
因为let具有块级作用域,因此在for循环内定义的迭代变量不会渗透到for循环外面。
for(let i = 0;i < 5;++ i){
setTimeout(()=>console.log(i),0);
输出结果为 0,1,2,3,4
let声明的迭代变量i不能渗透到for循环外面,因此可以认为是,声明了5个块级作用域,在每个块级作用域里面各自去执行自己的代码,互不干扰。类似以下代码:
{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;}
在每个块里面,同步代码(一次for循环)执行完就去执行宏任务(setTimeout)
而在书上看到的比较官方的解释是这样的:用var声明迭代变量时,在退出循环时,迭代变量保存的是导致循环退出的值:5。在之后执行超时逻辑时,所有的 i 都是同一个变量,因而输出的都是同一个最终值。
用 let 声明迭代变量时,JavaScript 引擎在后台会为每个迭代循环声明一个新的迭代变量。每个 setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循环执行过程中每个迭代变量的值。
粉丝