let和var傻傻分不清楚

293 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

让我们来写个for循环

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

这个代码看着好像没啥问题,我们预计的输出结果应该是 0,1,2,3,4

结果输出的居然是 5,5,5,5,5

出错了这么简单的代码居然出错了,还有天理吗?

"这里的var 应该改成 let 就可以了"一位不愿透露姓名的前端大佬路过

于是代码改成了如下:

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

这次代码正常运行输出了0,1,2,3,4

let和var有区别么?

let和var不都是声明变量么?有啥区别?

之前的前端大佬说:"ES6规定暂时性死区和let const语句不出现变量提升,主要是为了减少运行时的错误。"

你:"能说人话不?啥暂时性死区,啥又是变量提升。不对你还没回答我let和var有啥区别呢"

大佬:"你就理解var是全局变量,let是局部变量吧"

你:"局部变量和全局变量不是通过作用域区分的么?不是看括号范围的么?"

大佬:"我们还是聊聊暂时性死区吧"

什么是暂时性死区

当程序的控制流程在新的作用域(module function 或者block作用域)进行实例化时候, 在此作用域中的let/const 声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定, 也就是对声明语句进行求值运算,所以是不能访问的,访问就会抛出错误。所以在这运行流程一旦进入作用域创建变量, 到变量开始被访问之间的一段时间,称为TDZ。

简单来说就是:

  1. js运行程序时首先回创建let/const声明的变量
  2. js在运行到let/const 的赋值运算前 let/const声明的变量不能被访问

你:一个局部变量搞这么复杂,我理解let时创建局部变量了,那为啥for循环时var和let声明的变量i会执行效果不一样呢?

大佬:"嗯,这就是变量提升的问题了"

你:能一次说完么?禁止套娃

什么是变量提升

代码执行之前,会先扫描所有域内的var声明的变量,将其先进行初始化为undefined,然后再执行代码,也就是所谓的“提升”现象。

a = 'hello'
var a 
console.log(a) // 'hello'
console.log(b); // undefined 这里就是变量提升,还没声明就能用了
var b = 'word';

你: 大佬,我还是不明白为啥,var声明的和let声明的在for循环中表现不一样

大佬: 看下面的代码

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}

大佬:啥效果?

你: 打印了3次 abc

大佬: 为啥?let i = ‘abc’为什么可以声明3次?,let在一个作用域内不是不能声明同名变量么?

你: 我悟了,js的每对括号就是一个作用域,所以let声明时打印的每个变量都是独立的,而var声明的是全局的,所以var声明的变量打印出来就是一样的

写在最后

js真的是一门神奇的语言,千万别尝试用编译语言的逻辑去理解解释语言的逻辑。不过js真的很方便 ES6入门教程,拿去吧,打开新世界的大门!