你可能不是真的懂 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】。文章转载请联系作者。
评论