前言
使用ES6进行App和网页开发已经有一段的时间了,用法天天见,不知所以然。前几天同期的培森向我推荐了《深入理解ES6》这本书,真是一本好书,一个个短小精悍的代码片段仿佛对读者的一番番质问:嗨,你真的懂我吗??
如果说各种各样的ES6语法讲解是在陈述事实,那么本书更倾向于做一道证明题:为什么ES6是这样的?(为什么需要这样?)
帮助与支持:
阮一峰的《ES6入门》让我见识了ES6的魔法。
Zakas的《深入理解ES6》,揭开了魔法背后的玄机。
另外不得不提一下segmentdefault这篇问答,for循环里的let到底是怎么回事,我之前的认知一直都比较模糊,本文对这个操作做了去糖处理,整个思路非常清晰。
前置知识: 块级作用域的出现
ES6之前,js只划分全局作用域和函数作用域。如今,新增了块级作用域的概念。
let和const声明的变量,会自动绑定当前的块级作用域。外层作用域无法读取内层作用域的变量
let和const
let和const共有的特性归结起来有:
- 不存在变量提升
- 暂时性死区
- 声明的全局变量不是全局对象的属性
- 不允许重复声明
const独有的特性有:
- 声明时必须赋值
- 赋值后不允许修改(注意这个不允许修改指的是不允许修改指向的地址)
是什么和为什么
是什么
什么是变量提升
使用var关键字声明的变量,无论其实际声明位置在何处,都会被视为声明于所在函数的顶部。
如果声明不在函数内,则视为在全局作用域的顶部。
而let和const不存在变量提升。
暂时性死区
因为不存在变量提升,所有有了暂时性死区。
ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
暂时性死区,就是说在一个代码块里,你在let或const声明一个变量前去使用它,这时会报错。这个区块内变量声明前的区域就是暂时性死区。
为什么
为什么要变var为let和const
- 为了增添js的灵活性和与其它类c语言的一致性
- 因为var不够合理不够严谨,内层变量可能会覆盖外层变量,循环变量可能泄露为全局变量
为什么let和const声明的变量不绑定到全局对象
全局对象本身会有一些自己的属性,比如RegExp。
如果我们声明一个全局变量也叫RegExp,则会在无意间覆盖掉原有的RegExp。
于是我们将无法正常创建正则对象。。。。
不绑定到全局对象就不会有这种覆盖掉不该覆盖的的属性的风险了
坑: ES6如何翻译循环里的let
下面一段代码节选自《ES6入门》:
1 | for (let i = 0; i < 10; i++) { |
感谢块级作用域的存在,这段代码创建了六个不同的函数,每个函数里的i都不一样。
但是为什么会这样,阮一峰给出的解释略显模糊,也让一年前的我抓了瞎:
上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。
你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算。
具体到代码,如果我们给它做一个去糖处理,上面那段代码其实是这样的:
1 | var a = []; |
这样来看,阮一峰的话就好理解了。
js引擎如何记住上一轮循环的i值呢?答案是它不直接让i去做自增运算,i不过是保存了一个副本,真正自增的是引擎自己创造的一个专门用于存储每一次的状态的值。
于是每一次循环,我们都得到一个新的i值。