上周,JavaScript 语言的掌舵者们齐聚东京。在 Sony Interactive Entertainment 的主持下,Ecma 国际 TC39 委员会召开了第 104 次全体会议,一系列围绕 迭代器(Iterator) 和 Promise 的提案取得了重要进展。这些新特性将让 JavaScript 开发者写出更简洁、更函数式的代码。
让我们一起来看看这些即将改变你编码方式的新特性。
科普:TC39 与提案流程
在深入了解新特性之前,我们先来快速了解一下它们背后的组织和流程。
TC39 是什么?
TC39 (Technical Committee 39) 是 Ecma International 旗下的技术委员会,专门负责 ECMAScript 标准(即 JavaScript 的规范)的制定和维护。其成员由主流浏览器厂商(如 Google, Mozilla, Apple, Microsoft)以及其他对 Web 技术有影响力的科技公司和专家组成。
提案如何成为标准?
一个新特性从提出到最终进入标准,通常需要经历 5 个阶段(The TC39 Process):
- Stage 0 (Strawman - 稻草人): 任何想法或建议,用于开启讨论。
- Stage 1 (Proposal - 提案): 确定问题和解决方案,展示潜在的 API 形式。
- Stage 2 (Draft - 草稿): 具体的语法和语义描述,此时特性已基本定型。
- Stage 3 (Candidate - 候选): 规范文本已完成,需要浏览器厂商的实现反馈和用户测试。
- Stage 4 (Finished - 完成): 至少有两个独立的浏览器实现并通过了测试,准备纳入下一版 ECMAScript 标准。
大概需要多久?
这个过程没有固定的时间表,取决于提案的复杂度和争议程度:
- 简单特性:可能在 1-2 年内走完流程。
- 复杂特性:可能需要在 Stage 2 或 Stage 3 停留数年(例如 Temporal 提案)。
通常来说,一旦提案进入 Stage 3,就意味着它极有可能在不久的将来成为标准的一部分,各大浏览器也会开始逐步支持。
1. Iterator Sequencing(迭代器序列化)--- Stage 3
提案地址 : proposal-iterator-sequencing
解决什么问题?
你是否曾经需要把多个迭代器"串联"起来,当作一个来用?在以前,你得用生成器函数配合 yield* 来实现:
ini
let lows = Iterator.from([0, 1, 2, 3]);
let highs = Iterator.from([6, 7, 8, 9]);
// 以前的写法,略显繁琐
let combined = function* () {
yield* lows;
yield* highs;
}();
Array.from(combined); // [0, 1, 2, 3, 6, 7, 8, 9]
新写法
现在只需一行:
ini
let combined = Iterator.concat(lows, highs);
Array.from(combined); // [0, 1, 2, 3, 6, 7, 8, 9]
// 还可以在中间插入其他值
let digits = Iterator.concat(lows, [4, 5], highs);
Array.from(digits); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Iterator.concat() 接受多个可迭代对象,惰性地按顺序产出所有值。这不仅代码更简洁,而且因为是惰性求值,处理大数据集时内存效率也更高。
2. Await Dictionary of Promises(Promise 字典等待)--- Stage 1
提案地址 : proposal-await-dictionary
解决什么问题?
当你需要并发等待多个 Promise 时,Promise.all 返回的是数组,你得靠下标来取值:
scss
const [shape, color, mass] = await Promise.all([
getShape(),
getColor(),
getMass(),
]);
// 顺序错了?很容易出 bug
新写法
使用 Promise.allKeyed(),用对象的键来命名你的结果:
scss
const { shape, color, mass } = await Promise.allKeyed({
shape: getShape(),
color: getColor(),
mass: getMass(),
});
// 再也不用担心顺序问题了!
该提案还支持 Promise.allSettledKeyed(),用于处理可能失败的场景:
ini
const results = await Promise.allSettledKeyed({
shape: getShape(),
color: getColor(),
mass: getMass(),
});
if (results.shape.status === "fulfilled") {
console.log(results.shape.value);
} else {
console.error(results.shape.reason);
}
这个 API 设计有意与 Iterator.zipKeyed() 保持一致,体现了 TC39 在语言设计上的系统性思考。
3. Joint Iteration(联合迭代)--- Stage 2.7
提案地址 : proposal-joint-iteration
解决什么问题?
Python 开发者对 zip() 函数再熟悉不过了------它能把多个序列"拉链式"地组合在一起。JavaScript 一直缺少这个功能。
新写法
Iterator.zip() 和 Iterator.zipKeyed() 填补了这个空白:
php
// 位置对应的组合
Array.from(Iterator.zip([
[0, 1, 2],
[3, 4, 5],
]))
// 结果: [[0, 3], [1, 4], [2, 5]]
// 用键名组合
Iterator.zipKeyed({
a: [0, 1, 2],
b: [3, 4, 5, 6],
c: [7, 8, 9],
}).toArray()
// 结果: [
// { a: 0, b: 3, c: 7 },
// { a: 1, b: 4, c: 8 },
// { a: 2, b: 5, c: 9 }
// ]
还支持三种模式来处理长度不等的迭代器:
mode: 'shortest'(默认):最短的迭代器耗尽时停止mode: 'longest':最长的迭代器耗尽时停止,可以指定填充值mode: 'strict':如果长度不等则抛出错误
php
Iterator.zipKeyed({
a: [0, 1, 2],
b: [3, 4, 5, 6],
c: [7, 8, 9],
}, {
mode: 'longest',
padding: { c: 10 },
}).toArray()
// 结果包含: { a: undefined, b: 6, c: 10 }
4. Iterator Join(迭代器连接)--- 新提案
提案地址 : proposal-iterator-join
解决什么问题?
Array.prototype.join() 能把数组元素用分隔符连接成字符串。但如果你有一个迭代器呢?现在你得先转成数组:
csharp
myIterator.toArray().join(', ') // 先全部加载到内存
新写法
Iterator.prototype.join() 让你直接在迭代器上操作:
csharp
Iterator.from(['a', 'b', 'c']).join(', ')
// 结果: "a, b, c"
这对于处理大型或无限序列(只取部分)时特别有用,避免了不必要的内存分配。
5. Typed Array Find Within(类型数组范围查找)
解决什么问题?
现有的 TypedArray.prototype.indexOf() 只能从数组开头或指定位置向后搜索。当你需要在特定范围内查找时,现有 API 不够灵活。
新能力
这个提案为 TypedArray 添加了在指定范围内进行查找的能力,让二进制数据处理更加高效。这对于处理音视频数据、WebGL 缓冲区、以及其他需要精确控制搜索范围的场景非常有用。
迭代器生态的完善
如果你一直关注 TC39 的动态,你会发现这次会议推进的提案有一个共同主题:完善 JavaScript 的迭代器生态。
| 提案 | 功能 | 状态 |
|---|---|---|
| Iterator Helpers | map, filter, take, drop 等 | ✅ Stage 4(已标准化) |
| Iterator Sequencing | concat | Stage 3 |
| Joint Iteration | zip, zipKeyed | Stage 2.7 |
| Iterator Join | join | 新提案 |
| Await Dictionary | Promise.allKeyed | Stage 1 |
参考链接: