Object.propertyCount S1
- 阶段: Stage 1
- 状态: 进行中
- ECMAScript 版本: —
- 同步时间: 2026年8月26日
- English original · 官方仓库
该提案在 Object 上引入了三个新的静态方法(keysLength、getOwnPropertyNamesLength、getOwnPropertySymbolsLength),用于在不分配中间数组的情况下计算自身属性的数量,解决了如 Object.keys(obj).length 等常见模式带来的性能问题。这些方法镜像了现有 API(keys、getOwnPropertyNames、getOwnPropertySymbols)的语义,但直接返回计数。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
ECMAScript 提案:高性能对象属性计数
状态
提案倡导者:Ruben Bridgewater, Jordan Harband
作者:Ruben Bridgewater ruben@bridgewater.de, Jordan Harband ljharb@gmail.com
阶段:1
概述
本提案引入了三个内置方法,它们能够高效地获取对象自身属性的数量,而无需承担中间数组分配的性能开销:
Object.keysLength(target)——计算自身可枚举的字符串键属性(包括数组索引)的数量,与Object.keys对应。Object.getOwnPropertyNamesLength(target)——计算所有自身字符串键属性的数量,与Object.getOwnPropertyNames对应。Object.getOwnPropertySymbolsLength(target)——计算所有自身符号键属性的数量,与Object.getOwnPropertySymbols对应。
为了便于审查和推进,该规范被拆分为两个可独立推进的文档。其中一个用于 Object.keysLength,另一个用于其他两个方法的合并。
动机
开发者经常依赖这样的模式:
然而,这种方法会造成不必要的内存开销和垃圾回收压力,因为仅仅为了计数属性就分配了一个中间数组。高频使用的运行时、框架和库(例如 Node.js、React、Lodash、Angular、Storybook、Excalidraw、VS Code、Svelte、Next.js、three.js、Puppeteer、Tailwind 等)经常使用 Object.keys(obj).length,这加剧了跨应用的整体性能问题。
例如,React 经常对 props 或 state 键进行计数:
用原生且优化的计数方法替换这些模式,能显著减少内存开销、垃圾回收以及由此产生的运行时性能影响。
具体使用示例
问题陈述
目前,准确计算对象属性数量涉及冗长且低效的变通方法:
这会创建中间数组,导致不必要的内存使用和垃圾回收,影响应用性能——尤其是在大规模和性能关键的代码路径中。
这些 API 消除了常见计数模式中的中间数组,并与现有对应方法的语义直接对齐。
分解计划:三个独立的 API
为了促进增量式、可独立批准的进展,并与现有且易于理解的 API 紧密对齐,本提案将重写为三个聚焦的方法,它们是现有模式的即插即用、非分配等价物:
Object.keysLength(target)≈Object.keys(target).length(无中间数组)Object.getOwnPropertyNamesLength(target)≈Object.getOwnPropertyNames(target).length(无中间数组)Object.getOwnPropertySymbolsLength(target)≈Object.getOwnPropertySymbols(target).length(无中间数组)
每个 API 在下面单独的章节中使用当前结构(概述、提议 API、语义、示例、Polyfill)进行说明,以便它们能够独立推进。
Object.keysLength
概述
Object.keysLength 返回对象自身可枚举字符串键属性(包括数组索引属性)的数量,而不分配 Object.keys 产生的数组。
提议 API
明确的语义
- 如果
target不是对象,则抛出TypeError。 - 仅计算自身属性。
- 仅计算可枚举属性。
- 仅计算字符串键属性(包括数组索引)。
- 不分配任何中间数组。
示例
Polyfill(仅供参考)
Object.getOwnPropertyNamesLength
概述
Object.getOwnPropertyNamesLength 返回对象自身字符串键属性的数量,无论其可枚举性如何,而不分配 Object.getOwnPropertyNames 产生的数组。
提议 API
明确的语义
- 如果
target不是对象,则抛出TypeError。 - 仅计算自身属性。
- 仅计算字符串键属性(包括数组索引)。
- 同时计算可枚举和不可枚举的属性。
- 不分配任何中间数组。
示例
Polyfill(仅供参考)
Object.getOwnPropertySymbolsLength
概述
Object.getOwnPropertySymbolsLength 返回对象自身符号键属性的数量,而不分配 Object.getOwnPropertySymbols 产生的数组。
提议 API
明确的语义
- 如果
target不是对象,则抛出TypeError。 - 仅计算自身属性。
- 仅计算符号键属性。
- 无论属性是否可枚举都计算(与
Object.getOwnPropertySymbols一致)。 - 不分配任何中间数组。
示例
Polyfill(仅供参考)
详细示例和边界情况
- 空对象:
- 无原型的对象:
- 数组索引键:
参见 https://tc39.es/ecma262/#array-index
- 基于字符串的键:
- 基于符号的键:
明确的语义
- 仅考虑自身属性。
- 可枚举性语义与相应的现有 API 对齐:
Object.keysLength:仅计算可枚举字符串键属性(包括数组索引)。Object.getOwnPropertyNamesLength:计算所有字符串键属性,无论可枚举性。Object.getOwnPropertySymbolsLength:计算所有符号键属性,无论可枚举性。
- 实现必须完全避免中间数组分配。
算法规范
原生实现应严格避免创建中间数组或不必要的分配。对于每个方法:
- 将
count初始化为0。 - 直接遍历对象的自身属性键,通过其内部槽位,而不实例化数组。
- 对于每个自身属性键:
- 对于
Object.keysLength:如果键是字符串且属性可枚举,则递增count。 - 对于
Object.getOwnPropertyNamesLength:如果键是字符串(无论是否可枚举),则递增count。 - 对于
Object.getOwnPropertySymbolsLength:如果键是符号(无论是否可枚举),则递增count。
- 对于
- 返回
count。
参见规范文档的详细信息:
Object.keysLength:spec-keyslength.emuObject.getOwnPropertyNamesLength和Object.getOwnPropertySymbolsLength:spec-names-symbols-length.emu
考虑的替代方案
- 选择的方法:多个独立方法:本文档追求三个聚焦的方法(
Object.keysLength、Object.getOwnPropertyNamesLength、Object.getOwnPropertySymbolsLength),以最小化选项表面,与现有 API 形状对齐,并支持独立推进和评估。 - 带选项对象的单一方法(
Object.propertyCount(target, options)):之前已考虑并拒绝。虽然灵活,但它:- 产生微妙的组合语义(例如,可枚举性 + 键类型)。
- 偏离了聚焦、正交方法的既定先例(
keys、getOwnPropertyNames、getOwnPropertySymbols)。
- 使用布尔值表示键类型(在选项对象内):作为单一方法方法的一部分被拒绝,原因是默认值令人困惑且状态空间不匹配。
TC39 阶段和倡导者
- 准备进入 第 2 阶段
使用场景
- 改进的可读性和明确意图
- 显著的性能提升
- 减少内存开销
- 更简单的代码
先例
在广泛使用的 JavaScript 运行时、框架和库(Node.js、React、Angular、Lodash)中的频繁模式表明了对优化属性计数机制的共同需求。
正则表达式的 exec/match/matchAll 方法会产生一个“匹配对象”,它是一个数组,上面带有非索引的字符串属性(lastIndex、groups 等)。
考虑事项
- 向后兼容性:完全向后兼容。
- 性能:原生实现将通过消除中间数组显著优于现有方法。
- 灵活性:清晰、聚焦的方法覆盖了常见情况,无需配置复杂性。
- 简单性:提高代码可读性和清晰度。
- 面向未来:可以根据需要添加额外的聚焦 API(例如,可枚举符号的发现和计数),而不会用选项重载单一方法。
未来新增
以下添加是合理的后续工作,与现有方法家族和上述分解一致:
Object.symbols(target):一个新方法,类似于Object.keys(target),返回对象自身的可枚举符号键属性数组,按插入顺序排列。这填补了Object.keys(可枚举字符串键)和Object.getOwnPropertySymbols(所有自身符号,无论可枚举性)之间的当前空白。Object.symbolsLength(target):Object.symbols(target).length的非分配对应物,类似于Object.keysLength。这将被规范为与Object.keysLength完全相同,但仅限于可枚举符号键属性。
这些可以与上述三个方法独立地一起推进,并将为字符串和符号键提供一套完整、并行的原始方法:发现(keys/symbols)和计数(keysLength/symbolsLength),以及通过 getOwnPropertyNamesLength 和 getOwnPropertySymbolsLength 获得总的字符串/符号数量。
结论
Object.keysLength、Object.getOwnPropertyNamesLength 和 Object.getOwnPropertySymbolsLength 通过高效地计算对象属性数量而不使用中间数组,提供了实质性的性能优势,增强了 ECMAScript 的清晰度、性能和减少的内存开销,同时与易于理解的现有 API 对齐。