ES6中引入了let和const两个关键字,平时也就是知道let有块级作用域,const初始化之后不能更改,没有做过深究。今天测试demo时突然发现一个现象,一时没有想通,故特意记录,涨涨知识。问题如下:
问题说明
从上面可以看到我只是很简单地在JS中打印了let和var定义的变量,但是结果却跟我想的不一样,为什么let定义的变量在用this获取时会返回undefined呢?这个环境this不是都指向window吗?带着这个疑问我查找了一些资料,最终在阮一峰老师的文档下面找到了答案。在ES6中,用let命令、const命令、class命令声明的全局变量,不再属于顶层对象,不是顶层对象的属性,全局变量将逐步与顶层对象的属性脱钩,所以我代码中用let定义的变量,并不在window对象的属性之中,自然也就会输出undefined。
延伸学习
既然已经知道自己在let和const的认识浅薄,那当然得好好记录学习一下,避免以后踩坑,以下就是学习之后的系统总结。
let
块级作用域
ES5中,只存在全局作用域以及函数作用域,很多时候需要用到闭包去解决问题,还容易造成变量引用时的问题,为了解决这些,let带着块级作用域走来了。话不多说,先看代码:
从上面可以看出,大括号里面就是一个块级作用域,let定义的变量在该块级作用域之外访问时会发生报错,显示a并没有定义。这是很简单的一个块级作用域的例子,以下再给出一个稍微复杂的例子:
从这个例子也可以看出,内部块级作用域的变量也不会影响到外层变量。总结就是块级作用域内声明的变量,在作用域外不能被随便调用,否则报错,这个特性,const也是同理的,只不过const必须要初始化。
块级作用域是可以任意嵌套的,每一层都是一个单独的作用域,不同作用域下允许存在同名变量,但是也同样遵守上面的总结。例子如下:
除了以上这些,需要注意的是,ES6 的块级作用域必须有大括号,如果没有大括号,JavaScript 引擎就认为不存在块级作用域
变量提升
我们都知道,在ES5中,存在变量提升的现象,即变量的声明会被提前到开头,这么说可能有点抽象,还是举个例子解释:
很多人可能想当然以为这里应该输出2或者报错,但是结果是输出undefined。这个现象就是变量提升,即变量的声明语句会在程序执行时提前到程序的最前面,赋值的位置不变,实际上面这段代码执行时遵循的是下面的流程:
这样得出undefined这个结果就很显而易见了。
以上提到的是在ES6之前的做法,而用ES6的let关键字定义的变量,不含有变量提升的特性,也就是说变量的声明不会被提前,如果在声明之前去调用,将会报错,如下:
暂时性死区
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”。这个性质的表现跟let没有变量提升是相关的,这里不再举例,直接给出结论:暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
重复声明
在ES6之前,变量是可以重复声明的,后声明的变量会代替掉以前声明的变量,举例如下:
而在ES6中,使用let声明的变量在相同的作用域之下不能再次声明,否则会报错,所以在函数中,也不能去声明一个和参数同名的变量
const
const和let的区别主要就是const声明的变量不能再赋值,需要在声明时就初始化,其他特性与以上介绍的let特性一致,这里不予赘述。这里要说的是他另外的本质。const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。如果赋值的是一个对象或者数组时,const并不能限制其完全不变,实时上声明为const的对象或者数组,除了不能进行再次赋值之外,进行属性的增删改以及数组内容的增删是可以的。
以上就是本次记录的所有内容,参考了很多阮老师的案例,地址如下: