ECMAScript内部:JavaScript标准新增一个Stage

原文信息: 查看原文查看原文

Inside ECMAScript: JavaScript Standard Gets an Extra Stage

- Mary Branscombe

经过九年的年度JavaScript更新,TC39委员会调整了流程,以更快更平滑地推出新特性。

自JavaScript开始获得语言规范的新更新以来,已经将近十年——这一进程曾停滞多年,导致出现了大量框架和库以及一些非常有用的实验,但也让人们对语言的未来方向感到沮丧。ECMAScript 2015 通过交付不仅是语言有史以来最大的更新,而且是一个可靠的年度更新流程,结束了这种不确定性,这些更新带来了一系列大大小小的改进。

建立这一流程需要TC39委员会付出巨大努力,该委员会负责ECMAScript标准化。从将标准文档从单一的Word文档 转换为现代仓库——包括自定义的HTML方言——到决定一个多Stage 流程,确保新语言特性经过彻底设计、审查、测试和实施。目标是确保语言和浏览器社区的每个人都相信它们会起作用,改善语言,并且不会为开发者引入需要清理的不兼容性。

逐步确保质量和兼容性

对JavaScript中有用的新特性的建议从Stage 0提案开始,以探索想法并思考这些特性可能解决的问题。只有在它们既有一个包含它们为何有用的清晰解释的仓库,又有一个“冠军”来推动该特性所需的工作时,它们才能作为委员会打算审查的提案进入Stage 1。

Stage 2将初步设计进一步发展,代表了规范的初稿。将提案移至Stage 2意味着委员会期望该特性成为语言的一部分——但并不保证,因为处理细节、编写规范并提供初始实现(如polyfill)可能会发现难以轻易处理的问题。

“……渐进的里程碑,中间的里程碑,既认可进展,同时也完成了工作”。 ——Ecma副主席Daniel Ehrenberg

最初,Stage 3代表了一个几乎完成的候选提案,但需要从浏览器、服务器端运行时或开发者可以尝试的工具(如Babel和TypeScript)中获得实际体验的反馈。

这意味着为test262 ECMAScript一致性测试套件 提供一套完整的测试,以便至少有两个兼容的实现可以完成验收测试。虽然达到Stage 3并不正式要求test262测试,但Chrome不会在没有它们的情况下实现一个特性。因此,达到Stage 3通常(但并非总是)意味着一个特性不仅可以实现,而且还可以验证。

达到Stage 4意味着委员会同意规范的所有工作都已完成,并且已获得所有ECMAScript编辑的批准,准备被纳入整体语言规范。

“Stage 4是对我们在Stage 3所做的工作的承认,”Ecma副主席Daniel Ehrenberg 告诉The New Stack。整个过程旨在允许“逐步建立共识和逐步发展”,而不是等到提案足够完整到可以被批准和发布,他解释说,将Stage 描述为“渐进的里程碑,中间的里程碑,既认可进展,同时也完成了工作”。

使测试成为一个明确的里程碑

虽然流程运作良好,但一些大型提案最终需要比预期更多的工作,因为设计需要重做——在某些情况下,提案甚至从Stage 3退回到Stage 2。(这发生在模块和谐套件的一部分的导入属性提案上。)这不是坏事:这意味着JavaScript社区正在花时间确保特性正确,并确保它对开发者有用。

但根据原始流程,退回到Stage 2也意味着需要重写任何已经编写的测试,以便回到Stage 3,而编写测试是一项大量的工作,需要重复做两次——使得在Stage 之间移动比预期的要痛苦。

“Stage 3是我们说:我们认为设计已经完成,我们认为规范已经完成……” ——TC39联合主席Rob Palmer

“当我们到达Stage 3时,有一种尴尬,这[…]特别是对于大型提案,编写测试需要付出很多努力,”TC39联合主席Rob Palmer解释说。

“Stage 3是我们说,‘我们认为设计已经完成,我们认为规范已经完成,为了了解更多,我们必须实现[特性];是时候这么做了。’ 实现者真的希望在那个时候所有的测试都准备好了。但如果你正在处理一个规范,并且你正在努力解决最后一个设计点,并且你正在努力达到Stage 3,尽管实现者希望有测试,有时,因为努力,你还没有准备好做出那么大的投资。因为如果你得到设计反馈,如果设计不稳定,那么多的测试可能会失效,然后那就是浪费的努力。”

Stage 3旨在作为一个信号,表明一个提案已经准备好被实现——不是因为它已经完成,而是因为它足够成熟,可以证明更密集的实验是合理的,并且提案的设计在没有来自实现和测试的反馈之前尽可能地最终确定。但是,将实验、实现和验证结合在流程的同一Stage 可能会减慢进展,让提案在测试被重写时停滞不前,即使已经同意改变设计的决定。仅仅弄清楚哪些测试需要重写以适应设计变更就有很多工作。

达到Stage 2.7的提案在原则上被批准,但需要验证。

重复工作不仅落在特性提案的背后的人身上,也落在较少为人所知的test262委员会身上,Palmer指出。“测试工作量可能很大,而且很多工作是由不总是被支付来做这项工作的人完成的。有些人维护test262是因为他们想要为JavaScript做正确的事情,提高[语言特性的]质量。”

为了在2023年底将测试Stage 与实现(特别是在浏览器和JavaScript引擎中)分开,在Stage 2和Stage 3之间,TC39增加了一个新Stage :Stage 2.7。达到Stage 2.7的提案在原则上被批准,但需要验证:通过开发完整的测试套件和原型,并通过获得足够的经验来证明它可以被实现。在Stage 2.7,特性规范的文本是完整的,TC39委员会不会要求任何变化,除非是通过测试、实现和使用特性过程中出现的问题。

编号是精心选择的,以表明一个项目几乎,但还没有达到Stage 3。(有人建议以数学常数e命名,它的值约为2.7-something;虽然迷人,但那可能也会令人困惑且难以解释,但它可能帮助委员会选择了2.7而不是其他中间数字。)

“2.7实际上有我们过去对Stage 3的几乎相同要求,”Palmer告诉我们。“这意味着Stage 3现在实际上是一个更强的Stage ,因为它意味着Stage 3过去意味着的一切,但它也意味着‘我们有测试套件’……所以当实现者开始他们的实现时,他们有东西可以工作。这对他们来说是一个好信号,表明这个提案真的准备好开始了。”

Stage 3现在变成了获得实现经验并发现特性的任何Web兼容性或集成问题。除非有问题,否则不会再对特性规范进行更多更改。

这是一个重要的观点,因为当一个提案在达到Stage 3之前不得不改变其设计并重写测试时,有时会导致比预期更广泛的讨论,包括提出已经决定的问题,仅仅因为项目需要委员会批准测试的变更。

测试不阻碍进展

使测试成为Stage3的更明确要求并不像看起来那么简单,因为构建新语言特性并不是一个纯粹的顺序过程,你先设计,然后在设计正确后编写规范,然后在规范完成后编写测试——只有在那时才开始实现。

编写测试通常是思考特性设计的所有后果以及它如何需要被规范的最佳方式。

编写测试之所以如此工作量大,一个原因是编写测试通常是思考特性设计的所有后果以及它如何需要被规范的最佳方式。你不需要实现来考虑选项和影响。但一旦你编写了测试,你确实需要一个实现来运行它们,以便你可以验证测试是否确实正确编写。这意味着一个提案可能会多次经历一些步骤。

除了由提出特性的人创建的polyfill的早期实现外,一些实现者如Igalia和像Babel和TypeScript这样的转译器通常不会等到Stage 3。虽然浏览器和JavaScript引擎需要等到有测试套件,因为对他们来说实现的门槛更高,但有些测试可以针对原型实现如polyfill和转译代码编写。通过在网站上使用JavaScript代码的搜索、计数器,或者在Web浏览器的金丝雀构建中将早期实现放在特性标志后面,都可以更早地帮助检查Web兼容性问题(比如导致重写Map.groupBy和Object.groupBy方法在ECMAScript 2024中引入两次的冲突)。

同样,拥有test262测试套件并不意味着提案将自动进入Stage 3。委员会可能会决定它需要超出测试的更多信息(如Web兼容性数据)来证明有足够的经验来证明特性实际上可以被实现。

引入Stage 2.7并不一定意味着更少的提案会退回到一个Stage 。如果项目团队需要退后一步并重新考虑早期决策,这种情况仍然会发生。它确实使Stage 3对实现者来说风险较小,因为他们总是有测试套件,但它不能消除Web兼容性问题的所有风险。

Stage 2.7所做的是减少在生产完整测试套件然后必须更改时的冗余、浪费的工作。一个自信不需要任何更改的提案可以创建测试套件,并直接从Stage 2跳到Stage 3。事实上,添加内置机制将二进制数据转换为Base64的提案,Uint8Array到和从Base64,最近直接进入了Stage 3,尽管测试需要非常小的更新以匹配委员会的最终设计选择。

实践中的Stage 2.7

当TC39委员会增加了Stage 2.7时,他们查看了所有现有的Stage 3提案,虽然有几个项目没有检查完所有的测试,但它们都已经足够接近准备好,不需要降级(尽管长期延迟的Temporal提案可能会在稍微削减以避免在资源有限的设备上膨胀浏览器,如智能手表)。

已经有好几个提案已经推进到Stage 2.7,如延迟导入提案和Math.sumPrecise方法。

自从引入以来,已经有好几个提案推进到Stage 2.7,如延迟导入提案(大型模块和谐套件的另一部分)和用于在JavaScript中求和一系列值而不出现通常的浮点错误Math.sumPrecise方法。这是一个直接的提案,已经在TC39流程中快速推进,现在有了一套测试,并准备在委员会同意测试有足够的覆盖范围后进入Stage 3。期待已久的支持在正则表达式中转义字符串,Regexp.escape,已经达到Stage 2.7并随着其测试套件的到来已经进入Stage 3。

相比之下,“微等待”提案,现在称为atomics.pause,是很难编写任何有用测试的东西。它最近进入了Stage 2.7,大部分讨论涉及在规范中放入哪些注释来指导实现JavaScript引擎,以及注意到最终的测试基本上只会检查API是否存在。

并非所有提案都会像从Stage 2.7到3那样快速移动。ShadowRealm,这是一个长期提案,在过去15年的考虑中经历了很多修订,已经在引入Stage 2.7之前的TC39会议上被移回Stage 2。这个特性将为运行第三方脚本和插件提供隔离的执行环境,它们无法访问或修改主环境的状态。(值得指出的是,这更多是关于虚拟化和避免可能的冲突,而不是安全边界或沙箱,因为不同的代码片段可能使用相同的变量或函数名。)

为了从Stage 2推进,项目需要提出一系列浏览器和JavaScript运行时API,这些API需要暴露给ShadowRealm以使其工作,以及一套测试以确保实现中的正确行为,以及至少两个实现者的承诺,API列表和测试是他们需要进行实现的细节。

达到Stage 2.7表明ShadowRealm的设计实际上已经完成,但它要到达到Stage 3才能准备好实现和发布。

这些测试并不是ShadowRealm为了达到Stage 3所需要的完整测试套件,这使得ShadowRealm是为什么Stage 2.7如此有用的一个很好的例子。该提案能够在2024年2月推进到Stage 2.7,列出了API,但它仍然需要至少两个实现的明确支持——并且要达到Stage 3,它将需要包括WPT测试(Web平台测试跨浏览器测试套件)以实现浏览器集成。

这是一个复杂的提案,对WHATWG和WinterCG以及TC39都有影响。达到Stage 2.7表明ShadowRealm的设计实际上已经完成,但它要到达到Stage 3才能准备好实现和发布,因为仍然有集成问题需要解决。而这正是Stage 2.7旨在标记的中间进展的类型。

分享于 2024-09-07

访问量 68

预览图片