ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
&emsp;&emsp;在ES6之前的版本中,用于声明变量的关键字只有var,并且没有块级作用域,只有函数作用域和全局作用域,但在ES6中已改变这种状况。ES6引入了let和const两个关键字,它们既可以用于声明变量,还能够将变量绑定到当前所处的任意作用域中,换句话说,就是把变量的作用域封闭在所处的代码块(即花括号字符“{”和“}”之间的区域,例如if条件语句中的代码)中,如此一来就形成了块级作用域。let和const两个关键字与var之间的不同,简单的说有以下三点: &emsp;&emsp;(1)不允许声明提升。 &emsp;&emsp;(2)不允许重复声明。 &emsp;&emsp;(3)不覆盖全局变量。 &emsp;&emsp;而在let与const之间也有不同之处,在下面的内容中会重点讲解。 ## 一、let &emsp;&emsp;let可以理解为var的升级版本,摒弃或纠正了var的一些会导致代码混乱的特性,从而使得代码的逻辑更清晰,可维护性更高。 **1)提升** &emsp;&emsp;首先要介绍的是用let声明的变量,其声明语句不会再被提升。下面将两个变量分别用var和let声明,再输出它们的结果,具体如下所示。 ~~~ console.log(outer); //undefined console.log(inner); //抛出未定义的引用错误 { console.log(outer); //undefined console.log(inner); //抛出未定义的引用错误 var outer = true; let inner = true; console.log(outer); //true console.log(inner); //true } console.log(outer); //true console.log(inner); //抛出未定义的引用错误 ~~~ &emsp;&emsp;两个变量都在代码块中执行赋值操作。第一个outer变量由于是用var声明的,因此它的声明语句能够被提升,而在第一次和第二次输出时,因为还没被赋值,所以输出的结果都为undefined,在赋完值后,输出的结果就都是true。第二个inner变量是用let声明的,由于声明语句不能被提升到代码块的外部,因此它的作用域只限于代码块内,在代码块外就无法访问到它,并且在声明它之前也无法被访问(因为声明语句也不会被提升至作用域顶部),此时变量正处于临时死区中。如果强行访问,那么就会抛出未定义的引用错误。 &emsp;&emsp;临时死区(Temporal Dead Zone,简称TDZ)也叫暂时性死区,用let或const声明的变量,在声明之前都会被放到TDZ中,而在此时访问这些变量就会触发运行时错误。这种语法设计能促进工程师们在平时养成良好的编码习惯,减少或杜绝一些由于始料未及的原因而产生的程序BUG。有一点要注意,TDZ并不是由ECMAScript标准命名的,它是JavaScript社区的一种约定俗成的叫法。 **2)重复声明** &emsp;&emsp;接下来介绍let的第二个特性:不允许重复声明。注意,这里有一个前置条件,那就是只有在相同作用域时,才不允许同一个变量重复声明。ES6引入的这个重复声明的检查机制,可以避免在多人协作开发的时候,因多声明一个或多个同名的变量,而影响别人代码逻辑的情况出现。下面的示例就分了两个作用域分别说明,并且对比了var和let对待重复声明的处理结果。 ~~~ var duplicate; let repeat; var duplicate; //var声明的变量可重复声明 let duplicate; //抛出重复声明的语法错误 let repeat; //抛出重复声明的语法错误 { let repeat; //不同作用域,可正常声明 } ~~~ **3)全局作用域** &emsp;&emsp;最后介绍的是let在全局作用域中的特性。当用var在全局作用域中声明变量的时候,该变量不但会成为全局变量,而且还会成为全局对象(例如浏览器中的window对象)的一个属性。全局对象以及它的属性可以在任何位置被访问,这样就影响了函数的封装,不利于代码的模块化,并且新声明的全局变量有可能会覆盖全局对象中已存在的属性。上述是两个比较有代表性的弊端,为了解决这些问题,ES6规定用let可将全局变量和全局对象断开联系。下面用两组代码分别演示断开联系(第一组)和覆盖已有属性(第二组),注意,在第二组代码中为了方便对比,忽略了重复声明的错误。 ~~~ //第一组 var global = true; console.log(window.global); //true let whole = true; console.log(window.whole); //undefined //第二组 var Math = true; console.log(window.Math); //true let Math = true; console.log(window.Math); //Math对象 ~~~ ## 二、const &emsp;&emsp;const不但拥有上面所述的let的三个特性,并且还能声明一个常量。常量是指一个定义了初始值后固定不变的只读变量。在过去,常量都是通过命名规范(例如用大写字母、下画线等字符)定义的,虽然实现起来很便捷,但由于缺少约束,因此这种常量很容易被修改。而在ES6引入了const关键字后,就能像其它编程语言那样声明常量了。有一点要注意,const与let不同,在声明时必须初始化(即赋值),并且在设定后,其值无法再更改,如下代码所示。 ~~~ const number; //抛出未初始化的语法错误 const digit = 10; digit = 20; //抛出赋值给常量的类型错误 ~~~ &emsp;&emsp;此处要强调一点,const限制的其实是变量与内存地址之间的绑定,也就是说,const让变量无法更改所对应的内存地址。如果是基本类型(例如布尔值、数字等)的变量,那么对应的内存地址中保存的就是值;如果是引用类型(例如对象)的变量,那么对应的内存地址中保存的是指向实际数据的一个指针。由此可知,当用const声明的变量,其初始化的值是对象时,可以修改对象中的属性或方法,具体可参考下面的代码。 ~~~ const obj = {}; obj.name = "strick"; obj.age = function() { return 29; }; ~~~ ## 三、循环中的let和const &emsp;&emsp;ES6规定了let声明在循环内部的行为,以for循环为例,如下所示。 ~~~ for (let i = 0; i < 3; i++) { setTimeout(function() { console.log(i); }, 0); } ~~~ &emsp;&emsp;在控制台输出的结果依次为0、1、2,没有出现循环中的异步回调问题。这是因为在每次循环的时候,都会重新创建一个叫做i的同名变量,并将其初始化为计算后的值,而循环体内调用的i变量不会受其它同名变量的影响,所以能够在定时器的回调函数中正确显示该变量的值。在ES5及之前的版本中,如果要解决异步回调问题,就需要像下面这样借助立即执行函数表达式(IIFE)才能得到预期的效果。 ~~~ for (var i = 0; i < 3; i++) { (function(n) { setTimeout(function() { console.log(n); }, 0); })(i); } ~~~ &emsp;&emsp;const声明在循环内部的行为与let声明类似,不过,由于其值无法修改,因此在for循环中重新赋值(例如执行增量操作)将会抛出异常。但只要避开重新赋值,就能正常循环迭代,例如用for-in执行const声明的循环,如下所示。 ~~~ var author = { name: "strick", age: 29 }; for (const key in author) { console.log(key); } ~~~ ***** > 原文出处: [博客园-ES6躬行记](https://www.cnblogs.com/strick/category/1372951.html) [知乎专栏-ES6躬行记](https://zhuanlan.zhihu.com/pwes6) 已建立一个微信前端交流群,如要进群,请先加微信号freedom20180706或扫描下面的二维码,请求中需注明“看云加群”,在通过请求后就会把你拉进来。还搜集整理了一套[面试资料](https://github.com/pwstrick/daily),欢迎浏览。 ![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200) 推荐一款前端监控脚本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不仅能监控前端的错误、通信、打印等行为,还能计算各类性能参数,包括 FMP、LCP、FP 等。