JavaScript变量声明

 

var的缺陷

在说letconst前,我们先来看看var有哪些让人忍不住吐槽的地方。
传统的var声明的变量是函数级作用域
notion image
对于var声明的变量来说,只存在函数级作用域和全局作用域。
这种宽泛的作用域对于iffor的语法不太友好,试想一下以下代码:
notion image
你本来是想要获取-1却不小心获取到了在for代码块里声明的第二个i变量。
同时,从上面代码我们也能看到第二个var的缺点,就是在一个作用域里可以重复声明同一个变量
例如在下方代码里,index将会被重复声明,而js并不会报错。
notion image
var能重复声明的机制会让你很轻易地破坏原有的变量,在代码很少的时候或许你能谨慎地尽量不重复声明,但是当代码很多的时候,意外地重复声明变量是难以避免的。
此外var还存在着一种令人困惑的机制:变量提升,即变量可以在声明之前使用,值为undefined
notion image
js会先把var a 提升到作用域的顶部,然后到原代码位置时才执行赋值操作a = 1
正是由于var这些反人类的设计,ES6之后出现了新的声明变量的方式letconst

let

let是ES6新增语法,用来声明变量,作为var的替代品
let作为ES6新声明变量的方式,必定是要修复传统var的缺陷。
请记住letconst的主要特性
  • 块级作用域
  • 不能重复声明
  • 没有变量提升
  • 暂时性死区

什么是块级作用域

前面说过var是函数级作用域和全局作用域,那么什么是块级作用域呢?
我们可以先拆分一下: “块级” + “作用域”
作用域我们都熟悉,那么什么是块呢?
所谓的块,就是代码块,有一对大括号包裹的区域。
notion image

块级作用域

let声明的变量是块级作用域,换句话来说,就是let声明的变量的作用域在它声明时所在的代码块里
notion image
在块(作用域)外无法访问该变量,这与var声明的变量在函数外无法访问函数内声明的变量是一样的。

不能重复声明

在一个作用域里,无法用let重复声明同一个变量。
notion image
这样可以尽量避免变量被覆盖的情况。

不存在变量提升

前面说过var的变量提升机制,而let声明的变量则是必须声明后再使用,否则会报错。
notion image

暂时性死区

在了解暂时性死区前,先来看这一段有趣的代码
notion image
因为var会进行变量提升,所以其实在提升时第二个tmp就覆盖了第一个tmp变量,最终输出都是abc
但是我们把var换成let试一下
notion image
会报错 Uncaught ReferenceError: Cannot access 'tmp' before initialization
因为在if块里面let声明的变量tmp,就相当于“绑定”了这个区域,不再受外部的影响。因此tmp不能在声明前被访问,哪怕是作用域链中有同名变量。这就是暂时性死区
了解之后,我们这样改一下就不会报错了
notion image

总结

let是作为var的替代方法被设计的,因此它修复了var许多缺陷,而平时使用let应当牢记一个原则: 先声明再使用

const

基础用法

const也是ES6新语法,用作声明一个常量。
同时const也和let一样,具有块级作用域、不能重复声明等特性。
因为const声明的是常量,因此应当在声明时就赋值(初始化),不能像let一样,先声明再赋值。
notion image
const声明的变量也无法再重新赋值.

本质

const本质上并不是保证值不变,而是保证变量指向的那个内存地址不变。
要理解这句话,你可能需要先理解什么是原始类型和引用类型
在《JavaScript高级程序设计第四版》有一节专门讲了原始类型和引用类型,推荐阅读
js里的变量本质上是指针,指向一块内存地址,对于原始类型来说,变量指向的那块内存保存的就是它的值,因此变量指向的内存地址不变,变量的值也就不会改变;
但是对于引用类型来说(Object),变量指向的内存地址,保存的只是一个指向实际数据的指针,因此const只能保证变量指向的地址是固定的(即总是指向另一个固定的地址),但是不能保证变量指向的那个指针的指向不变,因此也就不能保证引用类型的值不变。
notion image

参考

部分内容参考自网道—ES6