JavaScript变量声明
var的缺陷
在说
let
和const
前,我们先来看看var
有哪些让人忍不住吐槽的地方。传统的
var
声明的变量是函数级作用域对于
var
声明的变量来说,只存在函数级作用域和全局作用域。这种宽泛的作用域对于
if
、for
的语法不太友好,试想一下以下代码:你本来是想要获取
-1
却不小心获取到了在for
代码块里声明的第二个i
变量。同时,从上面代码我们也能看到第二个
var
的缺点,就是在一个作用域里可以重复声明同一个变量例如在下方代码里,
index
将会被重复声明,而js并不会报错。var
能重复声明的机制会让你很轻易地破坏原有的变量,在代码很少的时候或许你能谨慎地尽量不重复声明,但是当代码很多的时候,意外地重复声明变量是难以避免的。此外
var
还存在着一种令人困惑的机制:变量提升,即变量可以在声明之前使用,值为undefined
js会先把
var a
提升到作用域的顶部,然后到原代码位置时才执行赋值操作a = 1
。正是由于
var
这些反人类的设计,ES6之后出现了新的声明变量的方式let
和const
let
let
是ES6新增语法,用来声明变量,作为var
的替代品let
作为ES6新声明变量的方式,必定是要修复传统var
的缺陷。请记住
let
和const
的主要特性- 块级作用域
- 不能重复声明
- 没有变量提升
- 暂时性死区
什么是块级作用域
前面说过
var
是函数级作用域和全局作用域,那么什么是块级作用域呢?我们可以先拆分一下: “块级” + “作用域”
作用域我们都熟悉,那么什么是块呢?
所谓的块,就是代码块,有一对大括号包裹的区域。
块级作用域
let
声明的变量是块级作用域,换句话来说,就是let
声明的变量的作用域在它声明时所在的代码块里在块(作用域)外无法访问该变量,这与var声明的变量在函数外无法访问函数内声明的变量是一样的。
不能重复声明
在一个作用域里,无法用
let
重复声明同一个变量。这样可以尽量避免变量被覆盖的情况。
不存在变量提升
前面说过
var
的变量提升机制,而let
声明的变量则是必须声明后再使用,否则会报错。暂时性死区
在了解暂时性死区前,先来看这一段有趣的代码
因为
var
会进行变量提升,所以其实在提升时第二个tmp
就覆盖了第一个tmp
变量,最终输出都是abc
。但是我们把
var
换成let
试一下会报错
Uncaught ReferenceError: Cannot access 'tmp' before initialization
因为在
if
块里面let
声明的变量tmp,就相当于“绑定”了这个区域,不再受外部的影响。因此tmp
不能在声明前被访问,哪怕是作用域链中有同名变量。这就是暂时性死区了解之后,我们这样改一下就不会报错了
总结
let
是作为var
的替代方法被设计的,因此它修复了var
许多缺陷,而平时使用let
应当牢记一个原则: 先声明再使用const
基础用法
const
也是ES6新语法,用作声明一个常量。同时
const
也和let
一样,具有块级作用域、不能重复声明等特性。因为const声明的是常量,因此应当在声明时就赋值(初始化),不能像
let
一样,先声明再赋值。被
const
声明的变量也无法再重新赋值.本质
const
本质上并不是保证值不变,而是保证变量指向的那个内存地址不变。要理解这句话,你可能需要先理解什么是原始类型和引用类型在《JavaScript高级程序设计第四版》有一节专门讲了原始类型和引用类型,推荐阅读
js里的变量本质上是指针,指向一块内存地址,对于原始类型来说,变量指向的那块内存保存的就是它的值,因此变量指向的内存地址不变,变量的值也就不会改变;
但是对于引用类型来说(Object),变量指向的内存地址,保存的只是一个指向实际数据的指针,因此
const
只能保证变量指向的地址是固定的(即总是指向另一个固定的地址),但是不能保证变量指向的那个指针的指向不变,因此也就不能保证引用类型的值不变。参考
部分内容参考自网道—ES6