ES 6 系列之Let 和 const 命令
let 命令
简单实用
ES6 中新增了 let
命令,用来声明变量。使用方式同 var
命令,但是 let
命令只在所在的代码块内有效。
1 | { |
在上面的使用案例中,我们可以看出 let
命令只在代码块中生效,而在代码块以外使用则无效。
for 循环中使用
我们来看看在 for
循环中使用 var
命令.
下面这段代码,打印出来的会是什么呢?可以跑一下代码,看一下控制台。
1 | for (var i = 0; i < 5; i++) { |
好了,你会发现,所有的打印都是 5
,为什么呢?是因为 var
命令是在全局范围内有效,所以全局只有一个变量 i
,每一次循环,变量 i
的值都会发生改变,所以当你打印是,打印的就是全局的 i
,也就是经过多次变化赋值的 5
。
上面代码中,我在外面打印了一下 i
,来证明 var
定义的是一个全局变量。
来,让我们将 var
改成 let
,现在打印的结果又是什么呢?
1 | for (let i = 0; i < 5; i++) { |
当然,我想很多人都猜到了结果,就是 0 1 2 3 4
,那么这里又是为什么呢?
let
变量只在所在的代码块内有效,在子代码块中依然有效。在上述循环中 i
只在本轮循环内有效,所以每一次循环的 i
其实都是一个新定义的变量,所以会输出当前 i
的值,每次循环对应输出。
当然,这里可能会出现一个问题,每轮的变量都是一个新变量,那它是怎么知道上一轮循环的值的?稍做解释:JS 引擎(v8)内部会记住上一轮循环的值,初始化本轮的变量时,就在上一轮循环的基础上进行计算。
上面我们说,let
命令在子代码块中也有效,可以从 for
循环上来看,()
内部其实就是一个父级作用域,而 {}
内部就是一个单独的子作用域。
不存在变量提升
变量提升,即变量可以在声明之前使用,值为 undefined
,var
命令存在,而 let
命令并没有变量提升,照样,我们来看看使用的案例。
1 | console.log(a); // undefined |
可以在控制台中跑一下这段代码,进行尝试。你会发现,a
变量进行了变量提升,而 b
变量没有发生,这表明了在声明之前使用,变量 b
是不存在的。
暂时性死区
代码块中,使用 let
命令声明变量之前,该变量都是不可用的。
在外部有一个全局变量 x
,但是在内部使用 let
声明了一个 x
,于是内部的 x
绑定了这个代码块,不受部影响。
但是在内部,声明变量之前的这个区域就是暂时性死区,我们来从下面的代码来看。
1 | var x = 100; |
暂时性死区的存在,也就意味着 typeof
的操作会变的不安全,在没有声明变量时,使用 typeof
并不会报错,返回一个 undefined
,然而因为暂时性死区,会使得 typeof
报错 ReferenceError
。
1 | if (true) { |
在函数默认值中也存在暂时性死区,而且会不容易发现。
1 | function add(x = y, y = 1) { |
在上面的代码中,就是如此,给函数赋默认值时,使用后面的值赋值给前面的 x
,而这时候 y
还没有定义,所以会报错。
不允许重复声明
var
变量是允许进行重复声明的,但是 let
变量是不允许的。
来,上代码。
1 | var a = 10; |
const 命令
const
命令 声明一个只读的常量。一旦声明,常量的值就不能改变
基本用法
1 | const PI = 3.14159; |
const
声明时,必须立即进行初始化,不能进行赋值。
1 | cosnt PI; // 报错 SyntaxError |
const
只声明,不赋值就会报错。
const
作用域与 let
相同:只在声明所在的块级作用域内有效。
1 | { |
const
命令也不存在变量提升,但是存在暂时性死区,只能在声明后使用。
与 let
一样,不可进行重复声明。
本质
首先,我们来看一个对象的例子:
1 | const obj = {}; |
由上面可以看出,常量 obj
储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把 obj 指向另一个地址,但对象本身是可变的,所以依然可以添加新属性。
再来看一个数组的例子:
1 | const arr = []; |
同对象,常量 arr
是一个数组,这个数组本身是可写的,但是如果将另一个数组赋值给 arr
,就会报错。
由此可见
const
保证的 实际上并不是变量的值不改动,而是变量指向的那个内存地址所保存的数据不得改动。const
只能保证这个指针式固定的,至于他指向的数据结构是不是可变的,就完全不能控制了。