WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit ad6187f

Browse files
committed
Finish initial proposal
1 parent 233aa02 commit ad6187f

File tree

4 files changed

+60
-56
lines changed

4 files changed

+60
-56
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2017 ECMA TC39 and contributors
3+
Copyright (c) 2025 Ruben Bridgewater
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const obj = { a: 1, b: 2 };
2121
const count = Object.keys(obj).length;
2222
```
2323

24-
However, this approach creates unnecessary memory overhead and garbage collection pressure, as an intermediate array is allocated solely for counting properties. Highly-used runtimes, frameworks, and libraries (e.g., Node.js, React, Lodash, Angular) frequently utilize `Object.keys(obj).length`, compounding performance issues across applications.
24+
However, this approach creates unnecessary memory overhead and garbage collection pressure, as an intermediate array is allocated solely for counting properties. Highly-used runtimes, frameworks, and libraries (e.g., Node.js, React, Lodash, Angular, Storybook, Excalidraw, VS Code, Svelte, Next.js, three.js, Puppeteer, Tailwind, ...) frequently utilize `Object.keys(obj).length`, compounding performance issues across applications.
2525

2626
For instance, React often counts props or state keys:
2727

@@ -239,33 +239,7 @@ The native implementation should strictly avoid creating intermediate arrays or
239239
- If the property meets all criteria, increment the counter.
240240
3. Return the final count value.
241241

242-
### Object.propertyCount ( _target_ [, _options_ ] )
243-
244-
When the `propertyCount` method is called, the following steps are taken:
245-
246-
**This is work in progress! The Algorithm is not yet fully defined.**
247-
248-
1. If Type(_target_) is not Object, throw a TypeError exception.
249-
2. If _options_ is undefined, let _options_ be an empty Object.
250-
3. Let _keyTypes_ be ? Get(_options_, "keyTypes").
251-
4. If _keyTypes_ is undefined, set _keyTypes_ to the array `['index', 'nonIndexString']`.
252-
5. Else, perform the following:
253-
1. If Type(_keyTypes_) is not Object, throw a TypeError exception.
254-
2. Set _keyTypes_ to an internal List whose elements are the String values of the elements of _keyTypes_.
255-
3. If _keyTypes_ contains any value other than "index", "nonIndexString", or "symbol", throw a TypeError exception.
256-
1. Let _enumerable_ be ? Get(_options_, "enumerable").
257-
2. If _enumerable_ is undefined, set _enumerable_ to true.
258-
3. Else if _enumerable_ is not one of true, false, or "all", throw a TypeError exception.
259-
4. Let _count_ be 0.
260-
5. Let _ownKeys_ be the List of own property keys of _target_, in the order returned by OrdinaryOwnPropertyKeys(_target_). (Note: No intermediate array should be allocated.)
261-
6. For each element _key_ of _ownKeys_, perform the following steps:
262-
1. Let _desc_ be ? OrdinaryGetOwnProperty(_target_, _key_).
263-
2. If _enumerable_ is not 'all'
264-
i. If _enumerable_ is unequal to _desc_.[[Enumerable]], continue to the next _key_.
265-
3. If Type(_key_) is Symbol and "symbol" is present in _keyTypes_, increment _count_ by 1.
266-
4. Else if Type(_key_) is array index and "index" is present in _keyTypes_, increment _count_ by 1.
267-
5. Else if "nonIndexString" is present in _keyTypes_, increment _count_ by 1
268-
7. Return _count_.
242+
See the [spec proposal](./spec.emu) for details.
269243

270244
## Alternatives Considered
271245

@@ -291,8 +265,16 @@ Frequent patterns in widely-used JavaScript runtimes, frameworks, and libraries
291265
```javascript
292266
const validTypes = new Set(['index', 'nonIndexString', 'symbol']);
293267

294-
Object.propertyCount = function(target, options = {}) {
295-
const { keyTypes = ['index', 'nonIndexString'], enumerable = true } = options;
268+
Object.propertyCount = function(target, options) {
269+
if (typeof target !== 'object' || target === null) {
270+
throw new TypeError(`Expected target to be an object. Received ${typeof target}`);
271+
}
272+
273+
if (options === undefined) {
274+
return Object.keys(target).length;
275+
}
276+
277+
const { keyTypes = ['index', 'nonIndexString'], enumerable = true } = options || {};
296278

297279
for (const type of keyTypes) {
298280
if (!validTypes.has(type)) {
@@ -307,13 +289,11 @@ Object.propertyCount = function(target, options = {}) {
307289
let props = [];
308290

309291
if (keyTypes.includes('index') || keyTypes.includes('nonIndexString')) {
310-
let stringProps = Object.getOwnPropertyNames(target);
292+
let stringProps = enumerable === 'all' ? Object.getOwnPropertyNames(target) ? Object.keys(target);
311293

312294
if (!keyTypes.includes('nonIndexString')) {
313295
stringProps = stringProps.filter(key => String(parseInt(key, 10)) === key && parseInt(key, 10) >= 0);
314-
}
315-
316-
if (!keyTypes.includes('index')) {
296+
} else if (!keyTypes.includes('index')) {
317297
stringProps = stringProps.filter(key => String(parseInt(key, 10)) !== key || parseInt(key, 10) < 0);
318298
}
319299

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
{
22
"private": true,
3-
"name": "template-for-proposals",
4-
"description": "A repository template for ECMAScript proposals.",
3+
"name": "object-property-count",
4+
"description": "Proposal for a fast way to count object properties.",
55
"scripts": {
66
"start": "npm run build-loose -- --watch",
77
"build": "npm run build-loose -- --strict",
88
"build-loose": "node -e 'fs.mkdirSync(\"build\", { recursive: true })' && ecmarkup --load-biblio @tc39/ecma262-biblio --verbose spec.emu build/index.html --lint-spec"
99
},
10-
"homepage": "https://github.com/tc39/template-for-proposals#readme",
10+
"homepage": "https://github.com/BridgeAR/object-property-count#readme",
1111
"repository": {
1212
"type": "git",
13-
"url": "git+https://github.com/tc39/template-for-proposals.git"
13+
"url": "git+https://github.com/BridgeAR/object-property-count.git"
1414
},
1515
"license": "MIT",
1616
"devDependencies": {

spec.emu

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,60 @@
44
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.4/styles/github.min.css">
55
<script src="./spec.js"></script>
66
<pre class="metadata">
7-
title: Proposal Title Goes Here
7+
title: Object.propertyCount
88
stage: -1
9-
contributors: Your Name(s) Here
9+
contributors: Ruben Bridgewater
1010
</pre>
1111

1212
<emu-clause id="sec-demo-clause">
13-
<h1>This is an emu-clause</h1>
14-
<p>This is an algorithm:</p>
13+
<h1>Object.propertyCount ( _target_ [ , _options_ ] )</h1>
14+
<p>When the `Object.propertyCount` method is called, the following steps are taken:</p>
1515
<emu-alg>
16-
1. Let _proposal_ be *undefined*.
17-
1. If IsAccepted(_proposal_) is *true*, then
18-
1. Let _stage_ be *0*<sub>ℤ</sub>.
19-
1. Else,
20-
1. Let _stage_ be *-1*<sub>ℤ</sub>.
21-
1. Return ? ToString(_stage_).
16+
1. If _target_ is not an Object, throw a TypeError exception.
17+
1. Let _resolvedOptions_ be ? GetOptionsObject(_options_).
18+
1. Let _keyTypes_ be ? Get(_resolvedOptions_, "keyTypes").
19+
1. If _keyTypes_ is undefined, then
20+
1. Set _keyTypes_ to the array `['index', 'nonIndexString']`.
21+
1. Else, perform the following,
22+
1. If _keyTypes_ is not an Object, throw a TypeError exception.
23+
1. Set _keyTypes_ to an internal List whose elements are the String values of the elements of _keyTypes_.
24+
1. If _keyTypes_ contains any value other than "index", "nonIndexString", or "symbol", throw a TypeError exception.
25+
1. Let _enumerable_ be ? Get(_resolvedOptions_, "enumerable").
26+
1. If _enumerable_ is undefined, set _enumerable_ to true.
27+
1. Else if _enumerable_ is not one of true, false, or "all", throw a TypeError exception.
28+
1. Let _count_ be 0.
29+
1. Let _ownKeys_ be the List of own property keys of _target_, in the order returned by OrdinaryOwnPropertyKeys(_target_). (Note: No intermediate array should be allocated.)
30+
1. For each element _key_ of _ownKeys_, perform the following steps, do
31+
1. Let _desc_ be OrdinaryGetOwnProperty(_target_, _key_).
32+
1. If _enumerable_ is not 'all', then
33+
1. If _enumerable_ is unequal to _desc_.[[Enumerable]], continue to the next _key_.
34+
1. If _key_ is Symbol and "symbol" is present in _keyTypes_, increment _count_ by 1.
35+
1. Else if _key_ is an array index and "index" is present in _keyTypes_, increment _count_ by 1.
36+
1. Else if "nonIndexString" is present in _keyTypes_, increment _count_ by 1.
37+
1. Return _count_.
2238
</emu-alg>
2339
</emu-clause>
2440

25-
<emu-clause id="sec-is-accepted" type="abstract operation">
41+
<!-- Copied from ECMA-402 GetOptionsObject -->
42+
43+
<emu-clause id="sec-getoptionsobject" type="abstract operation">
2644
<h1>
27-
IsAccepted (
28-
_proposal_: an ECMAScript language value
29-
): a Boolean
45+
GetOptionsObject (
46+
_options_: an ECMAScript language value,
47+
): either a normal completion containing an Object or a throw completion
3048
</h1>
3149
<dl class="header">
3250
<dt>description</dt>
33-
<dd>Tells you if the proposal was accepted</dd>
51+
<dd>
52+
It returns an Object suitable for use with GetOption, either _options_ itself or a default empty Object.
53+
It throws a *TypeError* if _options_ is not *undefined* and not an Object.
54+
</dd>
3455
</dl>
3556
<emu-alg>
36-
1. If _proposal_ is not a String, or is not accepted, return *false*.
37-
1. Return *true*.
57+
1. If _options_ is *undefined*, then
58+
1. Return OrdinaryObjectCreate(*null*).
59+
1. If _options_ is an Object, then
60+
1. Return _options_.
61+
1. Throw a *TypeError* exception.
3862
</emu-alg>
3963
</emu-clause>

0 commit comments

Comments
 (0)