你可能不是真的懂 let 和 const

在 ES5 我们习惯使用 var,而 var 常常会给我们带来一些困扰,比如存在变量提升、允许重复命名(且后执行的会覆盖前执行的)、没有暂时性死区、没有块级作用域 。而 ES6 新增的let命令const命令,解决了这些困扰。
let
下面通过代码的方式来解答,变量提升、重复命名、暂时性死区、块级作用域,这些问题的解决和功能添加让平时开发的我们得到什么帮助。
不存在变量提升
这样的代码,应该都见过吧?这就是变量提升。因为这种奇怪的行为其实代码是很难维护的。所以let解决了这样的问题。
既let不存在var那样的情况。当然要是直接使用let定义变量不赋值,使用的时候值当然也是undefined
不允许重复声明
使用var重复声明变量应该都是家常便饭了,但是let不允许在相同作用域内,重复声明同一个变量。
输出的结果大家应该都能明白,但是为什么上面加了大括号呢?
因为v8引擎做了一些特殊的处理 这里let,所以let在控制台中不加上大括号可以重复声明。
暂时性死区
只要块级作用域存在let命令,他所声明的变量就“绑定“这个区域,不再受外部的影响。
什么意思呢?就是使用let命令声明变量之前,该变量不可用。
还有一些比较隐蔽的错误,但是在开发中经常会遇到
这也是暂时性死区的好处,避免开发时的坏习惯,一定在变量声明后才能使用,否则报错。
块级作用域
内层变量可能会覆盖外层变量。
这段代码只是想在函数中打印外层的num,在if判断内打印出自己定义的num,可是打印出来的却是undefined,这是因为变量提升导致的,同时也是因为没有块级作用域。
没有块级作用域还会导致循环变量泄露为全局变量。
块级作用域不会影响全局作用域的好处。
为什么a能正常打印b却会报错?因为用var定义的变量不存在块级作用域也就是全局都能访问,只有在函数中使用var才存在作用域不影响全局,因此 ES5 中常常会听到函数作用域,但是使用let完全不需要担心,只要是大括号包住那么JavaScript就认为他存在块级作用域因此可以避免影响全局作用域。
为什么a[6]()会打印出10呢?
因为i是用var声明的,上面也说了,用var声明的变量是全局的,所以全局都能访问,每一次循环,变量i的值都在改变,且循环体内给数组赋值的函数内的i是全局的,所以输出的值是10。可能有的人还没明白或者会说这个应用场景是什么。那再来看看下面的例子
这样有应用场景了吧。我希望控制他过多久输出,可是为什么是打印4个5呢?这里涉及的知识点比较多(闭包,提升,事件循环),今天讲的是let就不要偏题了,后面我会单独讲解,拿这个出来讲是因为let恰恰能解决这个问题。
因为用let声明的变量,不是全局范围有效的,因此当前的i只在本轮循环中有效,所以每一次循环其实都是一个新值,所以他们互不干扰。可能还有人会问每次都是新值,那怎么知道当前循环的值呢?这就是JavaScript引擎内部做的事情了,现在只要知道有这件事即可,后面会单独讲解,毕竟涉及的知识点是比较多的,不要跑题了。
块级作用域声明函数
先说结果,ES5中当然打印出来的是inside,ES6中会提示fun is not a function 。应该都没问题。
但是这实际说明了什么?应该有些人没明白,那我来解释一下。先看看这个
在ES5中规定了,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域中声明。
后来ES6 引入了块级作用域,也明确了允许在块级作用域之中声明函数。ES6 规定,块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。可是需要兼容旧代码,所以附录B 里面也说明了浏览器可以不遵循上面的规则,有自己的行为方式。也就是
允许在块级作用域内声明函数
但是运行时会类似于
var,也就是会提升到全局作用域或函数作用域的头部。同时,函数声明还会提升到所在的块级作用域的头部。
需要注意:上面的规则只在ES6环境中实现有效,其他环境不需要遵守。
根据上面的规则再来看看ES6实际运行的效果
也就是说,在块级作用域中允许声明函数,但是又因为块级作用域的原因,且要兼容旧代码,所以块级作用域中的fun,没有被提升到外部,而提升到块级作用域的头部,这样应该都明白了吧。
const
const 是什么?const 声明的是一个常量,声明时必须同时赋值,且值不可变。
const与let 一样存在块级作用域、暂时性死区
其实const本质是保证变量所指向的内存地址不变,也就是说,引用类型中的属性值是可变,可添加的。
全局(window)
最后再来看看上面经常提起的全局到底说了什么。
全局作用域也叫做顶层对象,在浏览器中是window对象,在nodeJS中是global对象。前面经常会提到var定义的变量是全局可以访问的。先看代码
顶层对象与全局属性挂钩了。什么意思呢?
就是使用var命令和function命令声明的都是顶层对象的属性。所以能通过window找到对应的属性。ES6改变了这一点,但又为了保持兼容性,因此现在var和function声明的变量和函数依然属于顶层对象的属性,但是let、const、class声明的全局变量,不属于顶层对象的属性。
版权声明: 本文为 InfoQ 作者【前端树洞】的原创文章。
原文链接:【http://xie.infoq.cn/article/809010dbbfb06ed31959fd582】。文章转载请联系作者。











评论