4. 核心 ES6 特性
本章介绍了核心 ES6 特性。这些特性易于采用;其余特性主要对库作者有用。我将通过相应的 ES5 代码解释每个特性。
4.1 从 var
到 const
/let
在 ES5 中,您使用 var
声明变量。此类变量是*函数作用域*的,其作用域是最内层的封闭函数。var
的行为有时会令人困惑。这是一个例子
func()
返回 undefined
可能会令人惊讶。如果您重写代码以更准确地反映实际情况,您就会明白为什么
在 ES6 中,您还可以使用 let
和 const
声明变量。此类变量是*块级作用域*的,其作用域是最内层的封闭块。let
大致相当于块级作用域的 var
。const
的工作方式类似于 let
,但创建的变量的值不能更改。
let
和 const
的行为更加严格,并且会抛出更多异常(例如,当您在声明之前在其作用域内访问其变量时)。块级作用域有助于使代码片段的影响更加局部化(请参阅下一节以获取演示)。而且它比函数作用域更为主流,这使得在 JavaScript 和其他编程语言之间移动变得更容易。
如果在初始版本中将 var
替换为 let
,则会得到不同的行为
这意味着您不能在现有代码中盲目地将 var
替换为 let
或 const
;在重构过程中必须小心。
我的建议是
- 首选
const
。您可以将其用于所有值永不更改的变量。
- 否则,请使用
let
– 用于值会发生变化的变量。
- 避免使用
var
。
更多信息:“变量和作用域”一章。
4.2 从 IIFE 到块
在 ES5 中,如果您想将变量 tmp
的作用域限制为一个块,则必须使用一种称为 IIFE(立即调用函数表达式)的模式
在 ECMAScript 6 中,您可以简单地使用一个块和一个 let
声明(或 const
声明)
更多信息:“在 ES6 中避免使用 IIFE”一节。
4.3 从字符串连接到模板字面量
借助 ES6,JavaScript 终于获得了用于字符串插值和多行字符串的字面量。
4.3.1 字符串插值
在 ES5 中,您通过连接这些值和字符串片段将值放入字符串中
在 ES6 中,您可以通过模板字面量使用字符串插值
4.3.2 多行字符串
模板字面量还有助于表示多行字符串。
例如,这就是您在 ES5 中表示一个字符串所必须执行的操作
如果使用反斜杠转义换行符,则看起来会好一些(但您仍然必须显式添加换行符)
ES6 模板字面量可以跨越多行
(这些示例在包含多少空格方面有所不同,但在这种情况下无关紧要。)
更多信息:“模板字面量和标记模板”一章。
4.4 从函数表达式到箭头函数
在当前的 ES5 代码中,每当使用函数表达式时,您都必须小心 this
。在以下示例中,我创建了辅助变量 _this
(A 行),以便可以在 B 行中访问 UiComponent
的 this
。
在 ES6 中,您可以使用箭头函数,它不会遮蔽 this
(A 行)
(在 ES6 中,您还可以选择使用类而不是构造函数。这将在后面探讨。)
箭头函数对于仅返回表达式结果的简短回调特别方便。
在 ES5 中,此类回调相对冗长
在 ES6 中,箭头函数更加简洁
定义参数时,如果参数只是一个标识符,您甚至可以省略括号。因此:(x) => x * x
和 x => x * x
都是允许的。
更多信息:“箭头函数”一章。
4.5 处理多个返回值
某些函数或方法通过数组或对象返回多个值。在 ES5 中,如果您想访问这些值,则始终需要创建中间变量。在 ES6 中,您可以通过解构避免使用中间变量。
4.5.1 通过数组返回多个值
exec()
通过类数组对象返回捕获的组。在 ES5 中,即使您只对组感兴趣,也需要一个中间变量(以下示例中的 matchObj
)
在 ES6 中,解构使此代码更简单
数组模式开头的空槽跳过了索引为零的数组元素。
4.5.2 通过对象返回多个值
方法 Object.getOwnPropertyDescriptor()
返回一个*属性描述符*,这是一个在其属性中保存多个值的对象。
在 ES5 中,即使您只对对象的属性感兴趣,也仍然需要一个中间变量(以下示例中的 propDesc
)
在 ES6 中,您可以使用解构
{writable, configurable}
是以下内容的缩写
更多信息:“解构”一章。
4.6 从 for
到 forEach()
到 for-of
在 ES5 之前,您按如下方式迭代数组
在 ES5 中,您可以选择使用数组方法 forEach()
for
循环的优点是可以从中跳出,forEach()
的优点是简洁。
在 ES6 中,for-of
循环结合了这两种优点
如果您同时需要每个数组元素的索引和值,for-of
也可以通过新的数组方法 entries()
和解构来满足您的需求
更多信息:“for-of
循环”一章。
4.7 处理参数默认值
在 ES5 中,您可以像这样指定参数的默认值
ES6 具有更简洁的语法
另一个好处是,在 ES6 中,参数默认值仅由 undefined
触发,而在之前的 ES5 代码中,它由任何假值触发。
更多信息:“参数默认值”一节。
4.8 处理命名参数
在 JavaScript 中命名参数的一种常见方法是通过对象字面量(所谓的*选项对象模式*)
这种方法的两个优点是:代码变得更具描述性,并且更容易省略任意参数。
在 ES5 中,您可以按如下方式实现 selectEntries()
在 ES6 中,您可以在参数定义中使用解构,代码变得更简单
4.8.1 使参数可选
要在 ES5 中使参数 options
可选,您可以将 A 行添加到代码中
在 ES6 中,您可以将 {}
指定为参数默认值
更多信息:“模拟命名参数”一节。
4.9 从 arguments
到剩余参数
在 ES5 中,如果您希望函数(或方法)接受任意数量的参数,则必须使用特殊变量 arguments
在 ES6 中,您可以使用 ...
运算符声明剩余参数(以下示例中的 args
)
如果您只对尾随参数感兴趣,则剩余参数会更好
在 ES5 中处理这种情况很笨拙
剩余参数使代码更易于阅读:您只需查看函数的参数定义即可判断该函数是否具有可变数量的参数。
更多信息:“剩余参数”一节。
4.10 从 apply()
到展开运算符 (...
)
在 ES5 中,您可以通过 apply()
将数组转换为参数。ES6 具有用于此目的的展开运算符。
4.10.1 Math.max()
Math.max()
返回其参数中数值最大的一个。它适用于任意数量的参数,但不适用于数组。
ES5 – apply()
ES6 – 展开运算符
4.10.2 Array.prototype.push()
Array.prototype.push()
将其所有参数作为元素追加到其接收器。没有方法可以破坏性地将一个数组追加到另一个数组。
ES5 – apply()
ES6 – 展开运算符
更多信息:“展开运算符 (...
)”一节。
4.11 从 concat()
到展开运算符 (...
)
展开运算符还可以(非破坏性地)将其操作数的内容转换为数组元素。这意味着它可以替代数组方法 concat()
。
ES5 – concat()
ES6 – 展开运算符
更多信息:“展开运算符 (...
)”一节。
4.12 从对象字面量中的函数表达式到方法定义
在 JavaScript 中,方法是其值是函数的属性。
在 ES5 对象字面量中,方法的创建方式与其他属性相同。属性值通过函数表达式提供。
ES6 具有*方法定义*,这是一种用于创建方法的特殊语法。
更多信息:“方法定义”部分。
4.13 从构造函数到类
ES6 类主要只是构造函数的更方便的语法。
4.13.1 基类
在 ES5 中,您直接实现构造函数。
在 ES6 中,类为构造函数提供了稍微方便一些的语法。
请注意方法定义的紧凑语法 - 不需要关键字 function
。另请注意,类的各个部分之间没有逗号。
4.13.2 派生类
在 ES5 中,子类化很复杂,尤其是引用超级构造函数和超级属性时。这是创建 Person
的子构造函数 Employee
的规范方法。
ES6 通过 extends
子句内置了对子类化的支持。
更多信息:“类”一章。
4.14 从自定义错误构造函数到 Error
的子类
在 ES5 中,无法对内置的异常构造函数 Error
进行子类化。以下代码显示了一种解决方法,该方法为构造函数 MyError
提供了重要功能,例如堆栈跟踪。
在 ES6 中,所有内置构造函数都可以进行子类化,这就是以下代码能够实现 ES5 代码只能模拟的功能的原因。
更多信息:“对内置构造函数进行子类化”部分。
4.15 从对象到映射
在 JavaScript 中,使用语言结构*对象*作为从字符串到任意值的映射(一种数据结构)一直是一种权宜之计。最安全的做法是创建一个原型为 null
的对象。然后,您仍然必须确保没有任何键是字符串 '__proto__'
,因为该属性键会在许多 JavaScript 引擎中触发特殊功能。
以下 ES5 代码包含函数 countWords
,该函数使用对象 dict
作为映射。
在 ES6 中,您可以使用内置数据结构 Map
,并且不必转义键。缺点是,在映射中递增值不太方便。
映射的另一个好处是您可以使用任意值作为键,而不仅仅是字符串。
更多信息
4.16 新的字符串方法
ECMAScript 6 标准库为字符串提供了几种新方法。
从 indexOf
到 startsWith
。
从 indexOf
到 endsWith
。
从 indexOf
到 includes
。
从 join
到 repeat
(ES5 中重复字符串的方法更像是一种技巧)。
更多信息:“新的字符串功能”一章。
4.17 新的数组方法
ES6 中还有几种新的数组方法。
4.17.1 从 Array.prototype.indexOf
到 Array.prototype.findIndex
后者可用于查找 NaN
,而前者无法检测到。
顺便说一句,新的 Number.isNaN()
提供了一种检测 NaN
的安全方法(因为它不会将非数字强制转换为数字)。
4.17.2 从 Array.prototype.slice()
到 Array.from()
或展开运算符
在 ES5 中,Array.prototype.slice()
用于将类数组对象转换为数组。在 ES6 中,您可以使用 Array.from()
。
如果一个值是可迭代的(就像现在所有类数组 DOM 数据结构一样),您也可以使用展开运算符 (...
) 将其转换为数组。
4.17.3 从 apply()
到 Array.prototype.fill()
在 ES5 中,您可以使用 apply()
作为一种技巧来创建填充了 undefined
的任意长度的数组。
在 ES6 中,fill()
是一种更简单的替代方法。
如果您想创建一个填充了任意值的数组,fill()
甚至更方便。
fill()
会将所有数组元素替换为给定值。空洞的处理方式与元素相同。
更多信息:“创建填充了值的数组”部分。
4.18 从 CommonJS 模块到 ES6 模块
即使在 ES5 中,基于 AMD 语法或 CommonJS 语法的模块系统也已基本取代了手写的解决方案,例如揭示模块模式。
ES6 内置了对模块的支持。唉,目前还没有 JavaScript 引擎原生支持它们。但是,browserify、webpack 或 jspm 等工具可以让您使用 ES6 语法来创建模块,从而使您编写的代码面向未来。
4.18.1 多个导出
4.18.1.1 CommonJS 中的多个导出
在 CommonJS 中,您可以按如下方式导出多个实体。
或者,您可以将整个模块作为对象导入,并通过它访问 square
和 diag
。
4.18.1.2 ES6 中的多个导出
在 ES6 中,多个导出称为*命名导出*,其处理方式如下。
将模块作为对象导入的语法如下所示(A 行)。
4.18.2 单个导出
4.18.2.1 CommonJS 中的单个导出
Node.js 扩展了 CommonJS,并允许您通过 module.exports
从模块中导出单个值。
4.18.2.2 ES6 中的单个导出
在 ES6 中,同样的事情是通过所谓的*默认导出*(通过 export default
声明)完成的。
更多信息:“模块”一章。
4.19 下一步做什么
现在您已经初步了解了 ES6,您可以继续阅读各章来继续探索:每一章都涵盖了一个或一组相关的功能,并从概述开始。最后一章在一个位置收集了所有这些概述部分。