Promise 组合器
自从 ES2015 引入 Promise 后,JavaScript 支持的 Promise 组合器只有两个:静态方法 Promise.all
和 Promise.race
。
目前有两个新提案正在进行标准化过程:Promise.allSettled
和 Promise.any
。随着这些新增内容,JavaScript 中将总共有四种 Promise 组合器,每种都支持不同的使用场景。
自从 ES2015 引入 Promise 后,JavaScript 支持的 Promise 组合器只有两个:静态方法 Promise.all
和 Promise.race
。
目前有两个新提案正在进行标准化过程:Promise.allSettled
和 Promise.any
。随着这些新增内容,JavaScript 中将总共有四种 Promise 组合器,每种都支持不同的使用场景。
Array.prototype.flat
这里的数组是多层嵌套的:它包含一个数组,而这个数组又包含另一个数组。
const array = [1, [2, [3]]];
// ^^^^^^^^^^^^^ 外层数组
// ^^^^^^^^ 内层数组
// ^^^ 最内层数组
Array#flat
返回给定数组的扁平化版本。
array.flat();
// → [1, 2, [3]]
// …等价于:
array.flat(1);
// → [1, 2, [3]]
大型数字字面量对人眼来说难以快速解析,特别是当数字中有大量重复数字时:
1000000000000
1019436871.42
为了提高可读性,一个新的JavaScript语言特性允许在数字字面量中使用下划线作为分隔符。因此,上述内容现在可以改写为按千分进行分组,例如:
通常会在字符串上重复应用相同的正则表达式以获取所有匹配项。在一定程度上,现在可以通过使用 String#match
方法来实现这一点。
在这个例子中,我们找到所有仅包含十六进制数字的单词,并记录每个匹配项:
const string = '魔术十六进制数字:DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.match(regex)) {
console.log(match);
}
// 输出:
//
// 'DEADBEEF'
// 'CAFE'
然而,这只会给你匹配的子字符串。通常,你不仅仅想要子字符串,还需要其他信息,比如每个子字符串的索引或者每个匹配中的捕获组。
通过编写你自己的循环,并自己记录匹配对象也可以实现这一点,但这有点麻烦而且不太方便:
const string = '魔术十六进制数字:DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
console.log(match);
}
// 输出:
//
// [ 'DEADBEEF', index: 19, input: '魔术十六进制数字:DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: '魔术十六进制数字:DEADBEEF CAFE' ]
新的 String#matchAll
API 使得这一过程比以往更加简单:你现在可以编写一个简单的 for
-of
循环来获取所有的匹配对象。
const string = '魔术十六进制数字:DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.matchAll(regex)) {
console.log(match);
}
// 输出:
//
// [ 'DEADBEEF', index: 19, input: '魔术十六进制数字:DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: '魔术十六进制数字:DEADBEEF CAFE' ]
String#matchAll
对于带有捕获组的正则表达式特别有用。它为每个匹配提供完整的信息,包括捕获组。
const string = '喜欢的 GitHub 仓库:tc39/ecma262 v8/v8.dev';
const regex = /\b(?<owner>[a-z0-9]+)\/(?<repo>[a-z0-9\.]+)\b/g;
for (const match of string.matchAll(regex)) {
console.log(`${match[0]} 在 ${match.index} 上,输入为 '${match.input}'`);
console.log(`→ 所有者: ${match.groups.owner}`);
console.log(`→ 仓库: ${match.groups.repo}`);
}
现代的网页应用通常使用包含动态数据的列表。例如,一个照片查看器应用可能会显示如下内容:
此照片包含了 Ada、Edith 和 Grace。
而一个基于文本的游戏可能需要展示不同形式的列表:
选择你的超级能力: 隐身、心灵致动 或 共情。
由于每种语言具有不同的列表格式化惯例和用词,实现一个本地化列表格式化工具是极其复杂的。这不仅需要列出每种语言中所有的单词(如上述例子中的 “和” 或 “或”),还需要在编码时定义这些语言的具体格式化规则。Unicode CLDR 提供了这些数据,但在 JavaScript 中使用这些数据需要事先嵌入,并与其他库代码一起发送。这不幸会增加这些库的打包体积,从而对加载时间、解析/编译成本以及内存消耗产生负面影响。
在JavaScript模块中,已经可以使用以下语法:
import * as utils from './utils.mjs';
然而,之前没有对称的export
语法… 直到现在:
export * as utils from './utils.mjs';
多个提案扩展了现有的JavaScript类语法,新增了功能。本文解释了V8 v7.2和Chrome 72中的公共类字段的新语法,以及即将到来的私有类字段语法。
以下是一个创建名为IncreasingCounter
的类实例的代码示例:
const counter = new IncreasingCounter();
counter.value;
// 输出 '获取当前值!'
// → 0
counter.increment();
counter.value;
// 输出 '获取当前值!'
// → 1
注意,访问value
会执行一些代码(例如打印一条消息)然后返回结果。现在请思考,如何在JavaScript中实现这个类呢?🤔
以下是使用ES2015类语法实现IncreasingCounter
的方法:
class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('获取当前值!');
return this._count;
}
increment() {
this._count++;
}
}
该类在原型上安装了value
的getter和一个increment
方法。更有趣的是,类具有一个构造函数,该构造函数创建了一个实例属性_count
并将其默认值设置为0
。目前我们倾向于使用下划线前缀来表示_count
不应该被类的使用者直接使用,但这只是一种约定,并不是一种真正的由语言强制执行的“私有”属性。
JSON.stringify
之前被规范为在输入包含任何独立代理对时返回格式不良的 Unicode 字符串:
JSON.stringify('\uD800');
// → '"�"'
“格式良好的 JSON.stringify
”提案 修改了 JSON.stringify
,使其对独立代理对输出转义序列,从而使其输出为合法的 Unicode(且可表示为 UTF-8):
JavaScript 模块现在已经在所有主流浏览器中支持!
本文解释了如何使用 JS 模块、如何合理地部署它们,以及 Chrome 团队如何努力在未来使模块更加完善。
JS 模块(也称为“ES 模块”或“ECMAScript 模块”)是一个重要的新功能,或者说是一组新功能集合。你可能以前使用过用户级的 JavaScript 模块系统。可能使用过类似 Node.js 的 CommonJS,或者AMD,或者其他什么。这些模块系统都有一个共同点:它们允许你导入和导出内容。