导读:JavaScript 语言继续着保持发展的势头,即将推出最新版本的 ECMAScript,其中包含不少对开发者提供的新友好功能。
背景
好消息!ECMAScript 2025年度更新现在已经获得批准了。
我们现在来确切地知道哪些功能已纳入官方 JavaScript 语言规范,哪些功能仍在等待。它包含了一些开发者们期望的功能(但并非全部),以及一些快速通过审批阶段的新提案。
需要值得一提的是,2025年度更新仍被称为“ECMAScript”,不仅仅是因为负责制定该标准的 TC39 委员会是 ECMA International 的一部分,还因为Oracle 仍然持JavaScript 的注册商标。
Oracle 将于本月对Deno 要的索赔做出法律回应,Deno 称 Oracle 至今没有相关产品,它应该将JavaScript作为通用术语和废弃商标交还给开发者社区。
2025 版本有什么更新
自 ECMAScript 2015 发布以来,该语言本身继续以过去十年的势头不断地改进。
“这是一个健康规模的版本:不大不小,尺寸正合适。”
——罗布·帕尔默(Rob Palmer),TC39 联合主席
TC39 联合主席罗布·帕尔默(Rob Palmer)这样说道: “这是一个尺寸适中的版本:不大不小。它拥有许多符合人体工程学的特性,我们真心希望这些特性能够得到开发者的广泛应用。”
它们并不是基本的新功能,而是让 Web 开发人员的工作变得更加地轻松。帕尔默如此说,“这些功能让开发人员能够简洁地表达自己。”
JavaScript 中很早就有了迭代器的概念,但除了循环遍历值集合中的所有内容之外,它们就干不了了。如果开发者想要转换,甚至只是过滤值,需要使用第三方库或将值复制到数组中进行处理,这无疑会增加代码所需要的内存。
当你有了新的迭代器(Iterator)助手,刚才说的就不再需要了。
“现在你可以留在迭代器领域”,阿什利·克莱莫尔(Ashley Claymore,一位曾参与多项 TC39 提案的彭博软件工程师)表示,这将让语言更具表现力,而且通常更高效,因为“迭代器会尝试完成尽可能少的工作。”
如果你希望将迭代器过滤为前三个值,那么现在可以在控制循环中直接执行此操作。新的方法包括 map、filter、reduce、flatMap、some、find 和 every ,这些方法在使用数组时使用,你会很快熟悉,包括新的 drop 和 take 操作。
这就是我喜欢 JavaScript 的原因。我们不会回避这些事情。JavaScript 真的试图告诉你,这就是你最终能得到的。
——阿什利·克莱莫尔(Ashley Claymore,彭博软件工程师
这适用于同步迭代器。
最初,迭代器助手提案包含另一个用于处理包含Promise的迭代器功能;该功能被拆分为同步迭代器(为第 2 阶段提案),因为“一旦在其中引入异步操作,设计空间就会爆炸式增长”。另一个第 2 阶段迭代器提案,迭代器分块,将允许开发者从迭代器中检索多个值以进行同时处理(数据块或滑动窗口)。
正如预期的那样,用于组合和比较集合的新方法(这些方法在支持集合的语言中很常见,而 JavaScript 长期以来一直缺乏此功能)现在已成为 JavaScript 的一部分,填补了之前对于库来说太小且每个开发人员每次都难以正确完成的空白。在此之前,我只能向集合中添加值或检查某个值是否在集合中。“每个人都在一遍又一遍地编写这些小代码片段,”Palmer 这样指出道。
看似简单但实际上很棘手是该功能花费一段时间才实现的原因之一,但集合方法的结果定义明确(它们只需要像在数学和所有其他语言中那样工作),正确执行顺序也很重要。
“从抽象意义上讲,集合没有顺序,就像一袋东西;你不知道哪个是第一个,哪个是最后一个,”Claymore 继续解释道。“但在 JavaScript 中,你可以使用迭代器循环遍历集合,这样就有一个可观察的顺序。如果你遍历所有内容并记录下来,你就会看到哪些内容最先被记录,哪些内容最后被记录。”
重要的是,不要让某些东西在不同的实现中产生不同的结果。“这就是我喜欢 JavaScript 的原因,”Claymore 补充道。“我们不会回避这些问题。JavaScript 试图告诉你,这就是你最终会得到的结果。”
例如,虽然很容易说出两个集合的交集应该是什么,但计算的效率取决于如何处理不同大小的集合。
“你确实应该只循环遍历两者中较小的一个,”Claymore 说。“如果我有一个只包含一个元素的集合,和一个包含一百万个元素的集合,那么交集永远只会是零个或一个元素,所以最高效的做法是根据较小的集合进行排序——但这意味着排序取决于哪个集合较小。我们必须讨论这个问题:这样可以吗?对于开发者来说,有时会得到 123,有时会得到 231,这取决于哪个集合较小,这会让他们感到惊讶吗?”
最终,‘显著’的性能差异胜出,而非一致性。
与一致性相比,“显著的”性能差异占了上风,但几乎所有方法都做出了相同的决定,即排序是否应该直观或高效;而挖掘细节以做出这些决定需要时间。
此外,由于 JavaScript 的一大吸引力在于它是一种具有高度灵活性的动态语言,委员会需要决定如何严格定义哪些对象可以作为可以使用这些方法的集合。正如 Claymore 所指出的:“集合和数组,或者集合和映射的交集,因为它们的方法非常相似,所以可以吗?”
最终,归结为构成有效集合的三个关键属性。
“我们必须知道它的大小,”Claymore 说,“你必须能够对它执行 .size 操作,并且返回一个可以转换成整数的数字。然后它必须有两个方法:has 方法,这样我们就可以立即询问它,你拥有这个东西吗?然后还有一个 keys 方法,它为我们提供了一个迭代器,这样我们就可以循环遍历所有内容。”
这意味着你可以将新方法与具有这三种功能的映射一起使用,但不能与数组或字符串一起使用,因为它们没有此属性。
为 JavaScript 赋予CommonJS 模块全部功能的整套提案的制定工作仍然在继续。“我想说,至少部分基础工作已经落地,”帕尔默如此说道。
导入属性语法使用 with(不要与JavaScript 中更改范围的弃用 with 语句混淆)来导入 JSON 代码,并确信获得的 JSON 代码可处理潜在的安全问题。
“你可能认为您正在导入无害的数据,即常规 JSON,但在Web上,文件名只是一个文件名;它不能保证其中的内容,并且它可能正在传递 JavaScript,”Palmer 解释道。
最初的提议只是检查内容是否可以加载,但因为你可能使用此选项从 CSS 导入样式,而这需要修改标题(添加“Accept: text/css”),所以导入属性现在可以改变内容的加载方式;Palmer 认为这对于构建工具特别有用。
“我们已经吸取了教训 […] 特别是要确保 JavaScript 委员会和其他 Web 组织在流程早期就进行沟通。”
——Palmer
由于 Chrome 已使用原始 assert 关键字发布了该功能的早期版本,并且 Node 嵌入了 V8 引擎,因此工具生态系统已经采用了该功能。取消该功能的发布意味着需要使用遥测技术来查看有多少网站会受到影响——因为虽然其他浏览器尚未添加该功能,但 Chrome 的规模足够大,其功能能够快速被采用。
虽然当采用发生在工具而非网站代码中时,迁移会更容易,但对于工具开发者来说,这仍然是一项艰巨的工作。
“就跟印第安纳·琼斯的电影《夺宝奇兵》一样,他用一袋沙子替换了宝藏,我们侥幸逃脱了,”帕尔默说,“但显然,工具生态系统经历了迁移的阵痛,需要人们迁移过来。我们从中吸取了教训,特别是要确保 JavaScript 委员会和其他 Web 组织在流程早期就进行沟通;我们应该在第二阶段关注这些问题,因为一旦我们进入第三阶段,浏览器就有资格发布了。”
人们已经在基于导入属性进行构建。JSON模块提案详细说明了如何导入 JSON 并确保其符合预期(该提案最初是导入属性的一部分,并同时进入了第 4 阶段)。此外,还有一个名为“导入字节”的新提案,用于从任何类型的文件(例如,照片或要转换为 SVG 的Web 字体)导入任意字节,并且这些文件依赖于此。
“这是运行时已经表示我们想要使用这个功能的情况……”
——Claymore
在最近的 TC39 会议上,Import Bytes 直接从 Stage 0 跳升至 Stage 2,因为许多工具和运行时已经开始将其添加为一项功能。
Deno、Bun、webpack、esbuild、Parcel 和 Moddable 都支持该功能——但它们都使用不同的语法,因此开发者们必须检测其代码在哪个平台上运行,并选择正确的语法。
“这种情况是运行时已经表示我们想要使用这个功能了,”Claymore 解释道。“如果我们都要这么做,那就把它写进规范里,然后说明具体情况。”
如此大的兴趣意味着该功能可能也会迅速进入其他阶段。
重复名称捕获组,将允许开发者在正则表达式的两个部分中使用相同的名称,前提是其中只有一个部分能够匹配(这样你就不需要为了匹配 2025 或 25 而巧妙地为“年份”想出同义词了)。它与 ECMAScript 2024 稍有差距,因此今年很容易便通过了。
这并非正则表达式的唯一改进。
JavaScript长期以来需要一种方法来匹配和转义字符串中在正则表达式中具有特定含义的字符,例如 $ 或“。”; Claymore 这样说, Regex Escaping为开发人员提供了“你需要导入库或尝试自己编写但可能出错的东西”。
有时你希望正则表达式的一部分区分大小写,而其他部分不区分大小写。
——Claymore
JavaScript 还具有与其他语言和正则表达式引擎相同的语法,用于使用模式修饰符更改表达式中的大小写或多行标志。
“有时你希望正则表达式的一部分区分大小写,而另一部分不区分大小写,”Claymore 解释道。“过去,区分大小写与否是整个正则表达式的标志。现在你可以说:我希望除此部分之外的所有内容都不区分大小写,并且我希望此部分区分大小写。”
另一项是已经讨论过一段时间的提案,通过了添加Promise.try,获得了最终的进展,该提案用于将函数包装在promise中,而无需提前知道该函数(可能在库中)是否是异步的并且已经返回promise,或者仅仅是一个值。
“当你调用函数时,我们会遇到各种不同的可能性,”帕尔默如此解释道,“你很自然地希望获得 Promise 的结果,而不是抛出异常,因为这几乎是事情发展的第三种方式。Promise 可以以积极的方式实现,[但] 也可能被拒绝:因此,存在第三条可能抛出异常的控制路径就很奇怪了。”
Promise.try 意味着如果函数是同步的,它可以立即安全地运行。这节省了时间,但你无需编写额外的代码(或从 npm 拉取库)来实现这一点。使用新语法,TypeScript 也能更轻松地推断 JavaScript 中正在发生的事情。
“这更简单,代码在 TypeScript 中也能更好地运行,因此你可以获得另一个很好的人体工程学优势,”Claymore 补充道。
他表示,ECMAScript 2025 中的其他功能几乎会用在所有代码库中,而一个可能不太常用的低级功能就是新的半精度 Float16 TypedArray,它对于从事高级图形、WebGPU 和 WebGL 或机器学习的人来说非常受欢迎。它并没有为 JavaScript 添加新的数字类型,但这意味着你可以选择只存储 16 位而不是 64 位,从而节省一些内存。
“开发人员只需表达他们想要做的事情,系统就会尽可能快地运行。”
—— Claymore
“JavaScript 已经有 8 位整数、16 位整数、64 位整数、64 位浮点数和 32 位浮点数,但是它没有 16 位浮点数,”Claymore 说道,“而且在过去几年中,16 位浮点数的价值已经得到了充分的证明。在机器学习中,如果你能拥有两倍数量的数字,即使这些数字的精度只有一半,那也是一件非常好的事情。拥有更多权重的机器学习模型表现会更好,即使这些权重本身包含的信息较少。”
越来越多的 CPU 硬件开始支持 16 位浮点数,而这些浮点数也正是越来越多的 API 想要使用的,WebGPU 也能够处理这些浮点数,但 JavaScript 中却没有一个好的方法与这些 API 进行交互。“如果你尝试自己实现,那么获取和操作这些位的效率将会非常低,”Claymore 如此说道。
另外,你自己的代码不会自动利用添加支持的新 CPU,因此即使在新硬件上它也会仍然保持缓慢。
“现在你有了一个 Float16 数组,所以你可以原生地将 16 位浮点数降到 16 位,”Claymore 说,“开发者只需表达他们想要做的事情,它就会尽可能快地运行。”
作者:Mary Branscombe
编译:场长
参考:
https://thenewstack.io/javascript-standards-update-whats-new-in-ecmascript-2025
https://ecma-international.org/publications-and-standards/standards/ecma-262/
https://deno.com/blog/deno-v-oracle4
本文为 @ 场长 创作并授权 21CTO 发布,未经许可,请勿转载。
内容授权事宜请您联系 webmaster@21cto.com或关注 21CTO 公众号。
该文观点仅代表作者本人,21CTO 平台仅提供信息存储空间服务。