
ES2020已经发布,接下来将开始制定ES2021。本文将介绍截止2020年7月,已经进入TC39-Finished(Stage 4)阶段的提案和TC39-Candidate(Stage 3)的提案。
阿里巴巴前端委员会去年成为了Ecma官方成员,可以推进和阻止TC组织中的议题。但TC组织中的议题一旦进入Stage 2,其实就很难再阻止,除非是发现了重大的设计缺陷。TC39 7月20号会议agenda
Finished篇(Stage 4)
Finished(Stage 4)阶段的特性已经成为 ECMAScript 官方标准。这些特性基本不会再修改了,但并不是指已经可以大面积或者在生产环境中使用。而是意味着浏览器厂商已经部分实现,但可能还有很多地方需要打磨,具体的覆盖率可以通过 Can I Use 搜索查看。
Optional Chaining (?.) && Nullish Coalescing (??)
TC39提议链接:https://github.com/tc39/proposal-optional-chaining && https://github.com/tc39/proposal-nullish-coalescing
特性介绍:之所以将这2个特性合并一起讲,是因为他们的使用场景都是针对配置文件的处理:解决链式调用undefined查询 和 配置默认值。这2个特性看代码更容易理解。
支持度:Optional chaining Global 77.49% 和 Nullish coalescing Global 77.54%
代码示例:
/*config: {mysql: {server: {address: '192.168.172.155:3306',user: 'admin',pwd: 'pwd123',debug: false},client: {}}}*/// 从配置文件中读取mysql链接方式let mysqlConfig = config.get("mysql");let address = mysqlConfig?.server?.address ?? '127.0.0.1:3306';let user = mysqlConfig?.server?.user ?? 'admin';let password = mysqlConfig?.server?.pwd ?? 'pwd123';let needDebug = mysqlConfig?.server?.debug ?? true;const mysqlCLient = await connectMysql(address, {user, password, debug: needDebug});
BigInt
TC39提议链接:https://github.com/tc39/proposal-bigint
特性介绍:大整数特性让JS也支持高精度计算,超出JS的原生整数类型的精度安全边界运算。由Bloomberg提议,由Igalia推动,金融产品通常比较需要”大整数”支持。期待未来可以支持BigDecimal,解决JS一直被诟病的0.1+0.2 !== 0.3的问题。
支持度:Global 73.12%
代码示例:
let maxInt = Number.MAX_SAFE_INTEGER;// 9007199254740991
++maxInt;// 9007199254740992
++maxInt;// 9007199254740992
++maxInt;// 9007199254740992
let bigInt = 9007199254740991n;
++bigInt;// 9007199254740992n
++bigInt;// 9007199254740993n
bigInt*bigInt;// 81129638414606699710187514626049n
Promise.allSettled
TC39提议链接:https://github.com/tc39/proposal-promise-allSettled
特性介绍:对比Promise.all需要数组中的Promise全部resolved, Promise.allSettled 的数组中resolved 或 rejected 状态都可以,只要全部返回结果。
支持度:Global 81.53%
代码示例:
const settlePromiseArray = [ Promise.resolve(100), Promise.reject(null), Promise.reject(new Error('err'))];
Promise.allSettled(settlePromiseArray).then(results => {
console.log(results);
});
// [{"status":"fulfilled","value":100, {"status":"rej…"reason":null}, {"status":"rejected","reason":{}}]
globalThis
TC39提议链接:https://github.com/tc39/proposal-global
特性介绍:SSR的福音,在浏览器中全局变量window和Node.js全局变量global,可以通过globalThis统一。当然浏览器和Node.js特有的接口,globalThis不会同时支持。
支持度:Global 87.37%
代码示例:
// 浏览器中
globalThis.setTimeout === window.setTimeout;
// Node.js中
globalThis.setTimeout === global.setTimeout;
dynamic import
TC39提议链接:https://github.com/tc39/proposal-dynamic-import
特性介绍:JS中将支持动态import,import可以作为一个函数加载模块,支持then, catch链式调用。
支持度:Global 89.02%
代码示例:
/*
<!DOCTYPE html>
<nav>
<a href="books.html" data-entry-module="books">Books</a>
<a href="movies.html" data-entry-module="movies">Movies</a>
<a href="video-games.html" data-entry-module="video-games">Video Games</a>
</nav>
<main>Content will load here!</main>
*/
const main = document.querySelector("main");
for (const link of document.querySelectorAll("nav > a")) {
link.addEventListener("click", e => {
e.preventDefault();
import(`./section-modules/${link.dataset.entryModule}.js`)
.then(module => {
module.loadPageInto(main);
})
.catch(err => {
main.textContent = err.message;
});
});
}
Well defined for-in order
TC39提议链接:https://github.com/tc39/proposal-for-in-order
特性介绍:ES2020官方指定了for in的遍历顺序(之前并没有)。
Candidate篇(Stage 3)
Candidate阶段的特性确定需要实现,但要实现方和用户反馈打磨。在浏览器的先行版本和测试版本中,部分可用。
Top-level await
TC39提议链接:https://github.com/tc39/proposal-top-level-await
特性介绍:在顶级作用域直接使用await,看似简单的特性却影响了很多使用JS的场景。例如:import动态依赖路径;资源初始化;资源依赖回滚。另外如果模块依赖中使用了await,意味着后续的模块加载都会被block,也会block JS执行。
副作用:block JS执行;block 资源加载;无法在CommonJS modules中使用;同级模块并无法block加载;await发生在模块执行阶段,此时模块都已经获取和链接完毕;
代码示例:
传统的做法ES2015:
await Promise.resolve(console.log('🎉'));
// → SyntaxError: await is only valid in async function
(async function() {
await Promise.resolve(console.log('🎉'));
// → 🎉
}());
ES2020中:
await Promise.resolve(console.log('🎉'));
// → 🎉
// import动态依赖路径;
const strings = await import(`/i18n/${navigator.language}`);
// 资源初始化
const connection = await dbConnector();
// 资源依赖回滚
let jQuery;
try {
jQuery = await import('https://cdn-a.example.com/jQuery');
} catch {
jQuery = await import('https://cdn-b.example.com/jQuery');
}
WeakRefs && FinalizationRegistry
TC39提议链接:https://github.com/tc39/proposal-weakrefs
特性介绍:首先尽可能避免使用WeakRef和FinalizationRegistry,WeakRef和FinalizationRegistry可以分开使用也可以一起使用。WeakRef实例不会阻止GC回收,但是GC会在2次EventLoop之间回收”WeakRef实例”。GC回收后WeakRef实例的deref()方法将返回”undefined”。FinalizationRegistry注册Callback,某个对象被GC回收后调用。
副作用:JS引擎的GC很复杂,可能并不能如我们预期的回收对象。
代码示例:
let gListenerRegistry = new FinalizationRegistry(
({ socket, wrapper }) => socket.removeEventListener("message", wrapper);
);
function addWeakListener(socket, listener) {
let weakRef = new WeakRef(listener);
let wrapper = (ev) => { weakRef.deref()?.(ev); };
gListenerRegistry.register(listener, { socket, wrapper });
socket.addEventListner("message", wrapper);
}
class MovingAvg {
constructor(socket) {
this.events = [];
this.listener = (ev) => { this.events.push(ev); };
addWeakListener(socket, this.listener);
}
}
Private instance methods and accessors
TC39提议链接:https://github.com/tc39/proposal-private-methods
特性介绍:JS的Class引入了语言级别的私有属性,可以不用闭包来模拟了。JS作为面向原型语言,但随着TC39的不断推进,在向着更加成熟的语言进阶。
代码示例:
ES2015 只能在构造器中初始化this.xValue
class Counter extends HTMLElement {
get x() { return this.xValue; }
set x(value) {
this.xValue = value;
window.requestAnimationFrame(this.render.bind(this));
}
clicked() {
this.x++;
}
constructor() {
super();
this.onclick = this.clicked.bind(this);
this.xValue = 0;
}
connectedCallback() { this.render(); }
render() {
this.textContent = this.x.toString();
}
}
window.customElements.define('num-counter', Counter);
ES2020 可以直接定义属性
class Counter extends HTMLElement {
xValue = 0;
get x() { return this.xValue; }
set x(value) {
this.xValue = value;
window.requestAnimationFrame(this.render.bind(this));
}
clicked() {
this.x++;
window.requestAnimationFrame(this.render.bind(this));
}
constructor() {
super();
this.onclick = this.clicked.bind(this);
}
connectedCallback() { this.render(); }
render() {
this.textContent = this.x.toString();
}
}
window.customElements.define('num-counter', Counter);
ES2020 可以用#符号直接定义私有属性
class Counter extends HTMLElement {
#xValue = 0;
get #x() { return #xValue; }
set #x(value) {
this.#xValue = value;
window.requestAnimationFrame(this.#render.bind(this));
}
#clicked() {
this.#x++;
}
constructor() {
super();
this.onclick = this.#clicked.bind(this);
}
connectedCallback() { this.#render(); }
#render() {
this.textContent = this.#x.toString();
}
}
window.customElements.define('num-counter', Counter);
