for-in mechanics S4
中文标题:for-in 机制
- 阶段: Stage 4
- 状态: 已完成
- ECMAScript 版本: ES2020
- 同步时间: 2026年8月26日
- English original · 官方仓库
该提案旨在规范 for-in 循环的枚举顺序,此前在 ECMA-262 中几乎完全未指定。它定义了一组引擎已达成一致的保守互操作语义,涵盖常见情况,不包括外来对象或迭代期间的修改。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
本提案已被接受并合并到主规范。本仓库仅保留用于历史参考。
规范 for-in 枚举顺序
(部分地。)
ECMA-262 将 for (a in b) ... 的顺序几乎完全未指定,但实际引擎在至少某些情况下倾向于保持一致。此外,多年来实现者观察到,任何想在网络上运行代码的人都需要遵循一些规范未包含的约束。
这是一个阶段4(已完成)提案,旨在开始解决这个问题。
背景
历史上,试图就 for-in 顺序的完整规范达成共识的努力多次失败,部分原因是所有引擎都有自己独特的实现,这些实现是大量工作的结果,并且它们并不真正想重新审视。
参见 exploration 目录,了解在成为具体提案之前的背景和测试用例。
对互操作语义的保守低估
从这个互操作语义列表中,我们可以得出一个保守的、对引擎已经一致情况的低估,我相信这涵盖了最常见的情况。具体来说:
- 被迭代的对象或其原型链中的任何对象都不是代理、类型化数组、模块命名空间对象或宿主外来对象。
- 对象或其原型链中的任何对象在迭代期间都没有改变其原型。
- 对象或其原型链中的任何对象在迭代期间都没有删除属性。
- 对象原型链中没有任何属性在迭代期间被添加。
- 对象或其原型链中的任何属性的可枚举性在迭代期间都没有改变。
- 没有非可枚举属性遮蔽可枚举属性。
除最后一条外,其余都相当容易用散文形式说明;最后一条稍微困难一些。据我所知,JavaScriptCore 是唯一一个在这种情况下会输出内容的引擎,因为这个长期存在的bug,所以我希望这一点可以被丢弃。
影响
有多种API使得属性枚举顺序可观察。for-in 是最复杂的,因为它独特地也枚举来自原型链的属性。剩余的API可以分为:使用与 for-in 相同的顺序(针对自有属性)的API,这些受此提案影响;以及使用与 Reflect.ownKeys 相同的顺序的API,这些不受影响。
受 for-in 顺序影响的API
以下API使用 EnumerableOwnPropertyNames,它要求其结果“按照与调用 EnumerateObjectProperties 内部方法(使用[相关对象])时返回的迭代器所产生的相同相对顺序”排列。EnumerateObjectProperties 是 for-in 使用的规范内部方法,也是本提案旨在改进的部分。
Object.keysObject.valuesObject.entriesJSON.parse通过InternalizeJSONPropertyJSON.stringify通过SerializeJSONObject
other-effects 目录包含测试,演示了这些API如何被观察的简单示例。所有主流引擎在这些情况下已经达成一致。
因为 JSON.parse 产生的所有对象都在互操作语义范围内,本提案之后将完全指定其行为。其他API可以传入外来参数,因此不会完全指定。
受 Reflect.ownKeys 顺序影响的API
以下API直接调用 [[OwnPropertyKeys]] 内部方法,其行为已完全指定,因此不受本提案影响。
Reflect.ownKeysObject.getOwnPropertyNames(通过GetOwnPropertyKeys)Object.getOwnPropertySymbols(通过GetOwnPropertyKeys)Object.assignObject.create(通过ObjectDefineProperties)Object.defineProperties通过ObjectDefineProperties)Object.getOwnPropertyDescriptorsObject.freeze(通过SetIntegrityLevel)Object.seal(通过SetIntegrityLevel)Object.isFrozen(通过TestIntegrityLevel)Object.isSealed(通过TestIntegrityLevel)- 对象展开(通过
CopyDataProperties) - 对象剩余属性在赋值和绑定位置(通过
CopyDataProperties)
规范文本
参见候选规范文本。这尚未捕获上面提到的“没有非可枚举属性遮蔽可枚举属性”的约束,因为我难以想出如何表达它。