ES6 中的部分新特性

摘要:ES5 还没怎么掌握好,ES6 就来了(当然早就来了)。没办法,大势所趋啊。最近在学习使用 Vue.jsWebpack,其中就有很多 ES6 的写法。倒是不影响学习,但是既然新的标准出来了,一个合格的前端工程师又怎么可能没有兴趣了解和学习甚至固步自封呢。这里把一些知识点做一个汇总,以便随时查阅。

let 关键字

ES6 新增了 let 关键字用来声明变量。用法类似于 var,但是所声明的变量只在 let 所在的代码块内有效。for 循环的计数器就非常合适使用 let,这样计数器 i 只在 for 循环体内有效,在循环体外引用就会报错。

  • 没有变量提升:let 所声明的变量一定要在声明后使用,否则会报错。

  • 封闭作用域:只要块级作用域内存在 let 命令,这个区块里其声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量都会报错。注意:这里的报错是不是指报出 undefined,而是指 ReferenceError。所以即使使用 typeof 都不再是一个百分之百安全的操作。这样的设计是为了让人们养成良好的编程习惯,变量一定要在声明之后使用,否则就报错。

  • 不允许重复声明:不允许在相同作用域内重复声明同一个变量。

  • 定义的全局变量不再是顶层对象的属性。

const 关键字

声明一个只读的常量,一旦声明,该常量的值就不能改变。这意味着,用 const 一旦声明变量,就必须立即初始化,不能留到以后赋值。const 的其他限制同上面的 let 关键字。

const 实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。因此,将一个对象声明为常量必须非常小心。如果真的想将对象冻结,应该使用 Object.freeze 方法。

块级作用域

ES5 只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景。比如说,内层变量可能会覆盖外层变量,用来计数的临时的循环变量容易泄露为全局变量等等。而 ES6 允许块级作用域(用 {} 包裹的代码区域)的任意嵌套,外层作用域无法读取内层作用域的变量,内层作用域依然可以定义外层作用域的同名变量。

  • 允许在块级作用域之中可以声明函数。块级作用域之中,函数声明语句的行为类似于 let,在块级作用域之外不可引用,且声明时没有变量提升。所以应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句。

do 表达式

本质上,块级作用域是一组语句,将多个操作封装在一起,没有返回值。

而在块级作用域之前加上 do 就可以使它变为 do 表达式,然后就会返回内部最后执行的表达式的值。下面的例子中,变量 x 会得到整个块级作用域的返回值 w * w + h

let x = do {
  let w = 23, h = 8;
  w * w + h;
};

箭头函数

Module 模块

ES6 之前出现过一些模块加载方案,代表的有 CommonJSAMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上实现了模块功能,而且实现得相当简单,完全可以取代 CommonJSAMD 规范,成为浏览器和服务器通用的模块解决方案。

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJSAMD 模块都只能在运行时确定这些东西。因为只有运行时才能得到要加载的对象,导致完全没办法在编译时做到静态优化

ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,再通过 import 命令输入。ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。

除了静态加载带来的各种好处,ES6 模块还有以下好处:

  • 不再需要 UMD 模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。目前各种工具库已经做到了这一点。

  • 将来浏览器的新 API 可以用模块格式提供,不再必须做成全局变量或者 navigator 对象的属性。

  • 不再需要对象作为命名空间(比如 Math 对象),未来这些功能可以通过模块提供。

ES6 模块自动采用严格模式,不管有没有在模块头部加上 "use strict";。其中尤其需要注意 this 的限制:ES6 模块之中顶层的 this 指向 undefined,即不应该在顶层代码使用 this