"Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away." --- Antoine de Saint-Exupéry
6月30号,ECMAScript 2026 的规范悄悄过审了。
没有发布会,没有倒计时,连个热搜都没上。说实话,挺符合这次更新的气质------闷声干活,不整花里胡哨的。
打开 changelog 扫了一遍,我的第一反应不是"哇塞",而是------
"这玩意儿怎么现在才有?"
Map.getOrInsert:终于不用写三行废话了
写 JS 的应该都经历过这种场景:往 Map 里塞数据,key 存在就追加,不存在就初始化。
ES2026 之前,你的代码大概长这样:
javascript
const map = new Map();
function addTag(userId, tag) {
if (!map.has(userId)) {
map.set(userId, []);
}
map.get(userId).push(tag);
}
三行代码做一件事:检查、初始化、追加 。has 一次查找,get 又一次查找,加上 set 的那一次------恭喜你,为了一个简单的"有就拿、没有就建",你让引擎跑了三趟。
ES2026 之后:
javascript
const map = new Map();
function addTag(userId, tag) {
map.getOrInsert(userId, []).push(tag);
}
一行。结束。
而且还有个 getOrInsertComputed ,只在 key 不存在时才执行回调去算默认值。这对那些初始化成本高的场景来说,省的不只是代码行数,还有真金白银的运行时开销。
javascript
// 连接池场景:不存在才创建,存在直接复用
const pool = new Map();
const conn = pool.getOrInsertComputed(dbHost, (host) => {
return createConnection(host); // 只在首次才会执行
});
说白了,这个 API 就是把 Python 的 dict.setdefault() 和 Ruby 的 Hash#fetch 那套搬过来了。人家十几年前就有的东西,JS 到 2026 年才给你。
"不是JS用不起,而是TC39推得慢。"
Error.isError:instanceof 的坑,终于有人填了
你有没有遇到过这种灵异事件:一个东西明明是 Error,instanceof Error 却告诉你"不是"?
别怀疑自己。这是 JS 的经典设计遗产------跨 Realm 的原型链断裂。iframe 里抛出的 Error、Worker 里传递的 Error、甚至 vm.runInNewContext 里创建的 Error,它们的 Error 构造函数和你当前环境的 Error 压根不是同一个对象。
javascript
// iframe 里的代码抛了个错
const err = iframeWindow.someFunction();
console.log(err instanceof Error); // false
// 你盯着屏幕:???它明明就是个 Error 啊???
以前的应对方案通常是这样:
javascript
// "鸭子类型"检测法,能跑但心里没底
function isError(value) {
return value && typeof value.message === 'string'
&& typeof value.stack === 'string';
}
能用,但你心里清楚这就是在糊弄。任何带 message 和 stack 属性的对象都能骗过去。
ES2026 给了一个干净利落的解法:
javascript
// 跨 iframe、跨 Worker、跨 Realm,全部搞定
Error.isError(err); // true,不管你从哪来的
没有 polyfill 的弯弯绕绕,没有鸭子类型的心虚。原生方法,标准行为,一锤定音。
类似的设计其实早就有先例了------Array.isArray() 干的就是同样的事。只是 Error 这边拖了十几年才补上,算是还了一笔技术债。
Iterator.zip:拉链合并,不用再手搓索引了
同时遍历两个数组,按索引一一对应?以前你大概率是这么写的:
javascript
const names = ['Alice', 'Bob', 'Charlie'];
const scores = [92, 87, 95];
// 经典 for 循环手动对齐
const results = [];
for (let i = 0; i < Math.min(names.length, scores.length); i++) {
results.push([names[i], scores[i]]);
}
手搓索引,Math.min 防越界,每次写都觉得自己在重复劳动。Python 程序员看到这段代码大概会笑出声------人家一个 zip() 就搞定的事。
ES2026 终于把这个轮子给你造好了:
javascript
const names = ['Alice', 'Bob', 'Charlie'];
const scores = [92, 87, 95];
const results = Iterator.zip([names, scores]).toArray();
// [[Alice, 92], [Bob, 87], [Charlie, 95]]
而且给了三种模式,比 Python 的还周到:
javascript
// shortest(默认):以最短的为准
Iterator.zip([a, b]);
// longest:以最长的为准,短的用 undefined 填
Iterator.zip([a, b], { mode: 'longest' });
// strict:长度不一致直接报错,适合数据必须对齐的场景
Iterator.zip([a, b], { mode: 'strict' });
strict 模式是我最喜欢的。数据对不齐就直接炸给你看,别让那些 undefined 悄悄溜进你的业务逻辑里------等你发现的时候,线上已经出了两个工单了。
Math.sumPrecise:浮点数的坑,官方来填了
经典面试题:0.1 + 0.2 等于多少?
javascript
console.log(0.1 + 0.2); // 0.30000000000000004
这个坑每个 JS 开发者都踩过,但你有没有想过,当你需要对一个大数组做求和的时候,这些微小的精度误差会像滚雪球一样越积越大?
javascript
// 传统写法:误差在每次累加时都在积累
const total = values.reduce((sum, v) => sum + v, 0);
ES2026 的 Math.sumPrecise 用了更聪明的算法来做浮点求和------在运算过程中尽可能保持精度,最后才做一次舍入:
javascript
const values = [0.1, 0.2, 0.3, 0.4, 0.5];
Math.sumPrecise(values); // 1.5,干净利落
搞金融计算的、做数据统计的、写科学计算的------这个方法是给你们量身定做的。以后别再自己手搓 Kahan 求和算法了,标准库帮你兜底。
说点真心话
ES2026 不是那种让你"哇塞"的大版本。没有新语法糖,没有革命性的范式转移,没有能写进简历里炫耀的关键词。
但这恰恰是我觉得最舒服的一次更新。
每一个新 API 都在解决一个真实的、具体的、你写过一万遍 workaround 的痛点。
Map.getOrInsert 终结了 has-get-set 三件套。Error.isError 填上了 instanceof 的跨域天坑。Iterator.zip 让你不用再手搓索引对齐。Math.sumPrecise 让浮点求和不再是玄学。
这些API不酷,但是好用。就像一个老老实实修bug的同事------不会在周会上抢着发言,但你真心希望组里多来几个这样的人。
TC39 这次干了件正经事。
📊 数据来源与参考资料
- ECMAScript 2026 Language Specification: Ecma International, ECMA-262, 17th Edition (June 2026).
- TC39 Proposal: Map.getOrInsert : github.com/tc39/propos...
- TC39 Proposal: Error.isError : github.com/nicolo-riba...
- TC39 Proposal: Joint Iteration (Iterator.zip) : github.com/nicolo-riba...
- TC39 Proposal: Math.sumPrecise : github.com/nicolo-riba...
- JavaScript Weekly Issue 740 : javascriptweekly.com (July 2026)