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 84009af

Browse files
committed
Stage 2 preparation
1 parent a7f2c96 commit 84009af

File tree

2 files changed

+54
-70
lines changed

2 files changed

+54
-70
lines changed

README.md

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Status
44

5-
Champion: Jordan Harband
5+
Champion: Ruben Bridgewater, Jordan Harband
66

77
Author: Ruben Bridgewater <[email protected]>
88

@@ -155,8 +155,8 @@ Object.propertyCount(target[, options])
155155
- Throws `TypeError` if target is not an object.
156156
- **`options`** *(optional)*: An object specifying filtering criteria:
157157
- `keyTypes`: Array specifying property types to include:
158-
- Possible values: `'index'`, `'nonIndexString'`, `'symbol'`.
159-
- Defaults to `['index', 'nonIndexString']` (aligning closely with `Object.keys`).
158+
- Possible values: `'string'`, `'symbol'`, and `'all'`.
159+
- Defaults to `'string'` (aligning closely with `Object.keys`).
160160
- Throws `TypeError` if provided invalid values.
161161
- `enumerable`: Indicates property enumerability:
162162
- `true` to count only enumerable properties (default).
@@ -167,7 +167,6 @@ Object.propertyCount(target[, options])
167167
Defaults align closely with `Object.keys` for ease of adoption, ensuring intuitive behavior without needing explicit configuration in common cases.
168168

169169
The naming of keyTypes and if it's an array or an object or the like is open for discussion.
170-
Important is just, that it's possible to differentiate index from non index strings somehow, as well as symbol properties.
171170

172171
Similar applies to the enumerable option: true, false, and `'all'` seems cleanest, but it's not important how they are named.
173172

@@ -198,25 +197,25 @@ Object.propertyCount(obj2); // returns 1
198197
See https://tc39.es/ecma262/#array-index
199198

200199
```js
201-
let obj = { '01': 'string key', 1: index, 2: 'index' };
202-
Object.propertyCount(obj, { keyTypes: ['index'] }); // returns 2
200+
let obj = { "01": "string key", 1: "index", 2: "index" };
201+
Object.propertyCount(obj, { keyTypes: ['string'] }); // returns 3
203202

204-
obj = { '0': 'index', '-1': 'string key', '01': 'string key' };
205-
Object.propertyCount(obj, { keyTypes: ['index'] }); // returns 1 (only '0')
203+
obj = { "0": "index", "-1": "string key", "01": "string key" };
204+
Object.propertyCount(obj, { keyTypes: ['string'] }); // returns 3
206205
```
207206

208207
- **String based keys**:
209208

210209
```js
211-
const obj = { '01': 'string key', 1: 'index', 2: 'index' };
212-
Object.propertyCount(obj, { keyTypes: ['nonIndexString'] }); // returns 1
210+
const obj = { "01": "string key", 1: "index", 2: "index" };
211+
Object.propertyCount(obj, { keyTypes: ['string'] }); // returns 3
213212
```
214213

215214
- **Symbol based keys**:
216215

217216
```js
218-
const obj = { [Symbol()]: 'symbol', 1: 'index', 2: 'index' };
219-
Object.propertyCount(obj, { keyTypes: ['symbol'] }); // returns 1
217+
const obj = { [Symbol()]: "symbol", 1: "index", 2: "index" };
218+
Object.propertyCount(obj, { keyTypes: ['symbol'] }); // returns 2
220219
```
221220

222221
## Explicit Semantics
@@ -233,7 +232,7 @@ The native implementation should strictly avoid creating intermediate arrays or
233232
2. Iterate directly over the object's own property descriptors
234233
- Access the internal property keys directly via the object's internal slots.
235234
- For each own property:
236-
- Determine if the key is a numeric index, a regular non-index string, or a symbol.
235+
- Determine if the key is a string or a symbol.
237236
- Check if the property type matches any specified in `keyTypes`.
238237
- If `enumerable` is not `'all'`, match the property's enumerability against the provided boolean value.
239238
- If the property meets all criteria, increment the counter.
@@ -244,38 +243,40 @@ See the [spec proposal](./spec.emu) for details.
244243
## Alternatives Considered
245244

246245
- **Multiple separate methods**: Rejected due to increased cognitive load and API complexity.
246+
- **Using booleans for key types**: The default would have diverging boolean defaults for different key types (in other words, 4 possible combinations for only 3 possible states). Thus, an enum is considered a better approach.
247247

248248
## TC39 Stages and Champion
249249

250-
- Ready for **Stage 1** (proposal)
250+
- Ready for **Stage 2**
251251

252252
## Use Cases
253253

254254
- Improved readability and explicit intent
255-
- Significant performance gains
256-
- Reduced memory overhead
257-
- Simpler code
255+
- Significant **performance** gains
256+
- **Reduced memory** overhead
257+
- **Simpler code**
258258

259259
## Precedent
260260

261261
Frequent patterns in widely-used JavaScript runtimes, frameworks, and libraries (Node.js, React, Angular, Lodash) demonstrate the common need for an optimized property counting mechanism.
262262

263+
The regular expression exec/match/matchAll methods produce a "match object" that is an Array, with non-index string properties on it (lastIndex, groups, etc).
264+
263265
## Polyfill
264266

265267
```js
266-
// NOTE: do not use this polyfill in a production environment
267-
const validTypes = new Set(['index', 'nonIndexString', 'symbol']);
268+
const validTypes = new Set(['string', 'symbol']);
268269

269-
Object.propertyCount = function (target, options) {
270+
Object.propertyCount = function propertyCount(target, options) {
270271
if (typeof target !== 'object' || target === null) {
271272
throw new TypeError(`Expected target to be an object. Received ${typeof target}`);
272273
}
273274

274-
if (options === undefined) {
275+
if (typeof options === 'undefined') {
275276
return Object.keys(target).length;
276277
}
277278

278-
const { keyTypes = ['index', 'nonIndexString'], enumerable = true } = options || {};
279+
const { keyTypes = ['string'], enumerable = true } = options || {};
279280

280281
for (const type of keyTypes) {
281282
if (!validTypes.has(type)) {
@@ -289,16 +290,8 @@ Object.propertyCount = function (target, options) {
289290

290291
let props = [];
291292

292-
if (keyTypes.includes('index') || keyTypes.includes('nonIndexString')) {
293-
let stringProps = enumerable === true ? Object.keys(target) : Object.getOwnPropertyNames(target);
294-
295-
if (!keyTypes.includes('nonIndexString')) {
296-
stringProps = stringProps.filter(key => String(parseInt(key, 10)) === key && parseInt(key, 10) >= 0);
297-
} else if (!keyTypes.includes('index')) {
298-
stringProps = stringProps.filter(key => String(parseInt(key, 10)) !== key || parseInt(key, 10) < 0);
299-
}
300-
301-
props = stringProps;
293+
if (keyTypes.includes('string')) {
294+
props = enumerable === true ? Object.keys(target) : Object.getOwnPropertyNames(target);
302295
}
303296

304297
if (keyTypes.includes('symbol')) {
@@ -319,6 +312,7 @@ Object.propertyCount = function (target, options) {
319312
- **Performance**: Native implementation will significantly outperform existing approaches by eliminating intermediate arrays.
320313
- **Flexibility**: Enumerable properties counted by default; easy inclusion/exclusion.
321314
- **Simplicity**: Improved code readability and clarity.
315+
- **Future proofing**: The second argument is an options object and this potentially allows future additions (e.g., to include inherited properties, or only writable properties, etc).
322316

323317
## Conclusion
324318

spec.emu

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,47 +5,37 @@
55
<script src="./spec.js"></script>
66
<pre class="metadata">
77
title: Object.propertyCount
8-
stage: 0
8+
stage: 1
99
contributors: Ruben Bridgewater, Jordan Harband
1010
</pre>
1111

12-
<emu-clause id="sec-fundamental-objects" number="20">
13-
<h1>Fundamental Objects</h1>
14-
15-
<emu-clause id="sec-object-objects">
16-
<h1>Object Objects</h1>
17-
18-
<emu-clause id="sec-properties-of-the-object-constructor" number="2">
19-
<h1>Properties of the Object Constructor</h1>
20-
21-
<emu-clause id="sec-object.propertycount" number="20">
22-
<h1>Object.propertyCount ( _target_ [ , _options_ ] )</h1>
23-
<p>When the `Object.propertyCount` method is called, the following steps are taken:</p>
24-
<emu-alg>
25-
1. If _target_ is not an Object, throw a TypeError exception.
26-
1. Let _resolvedOptions_ be ? GetOptionsObject(_options_).
27-
1. Let _keyTypes_ be CreateArrayFromList(« *"index"*, *"nonIndexString"* »).
28-
1. Let _keyTypesOption_ be ? Get(_resolvedOptions_, *"keyTypes"*).
29-
1. If _keyTypesOption_ is not *undefined*, then
30-
1. If _keyTypesOption_ is not an Object, throw a TypeError exception.
31-
1. Set _keyTypes_ to ? CreateListFromArrayLike(_keyTypesOption_).
32-
1. If _keyTypes_ contains any value other than *"index"*, *"nonIndexString"*, or *"symbol"*, or if any of those values are repeated, throw a TypeError exception.
33-
1. Let _enumerable_ be ? Get(_resolvedOptions_, *"enumerable"*).
34-
1. If _enumerable_ is *undefined*, set _enumerable_ to *true*.
35-
1. If _enumerable_ is not one of *true*, *false*, or *"all"*, throw a TypeError exception.
36-
1. Let _count_ be 0.
37-
1. Let _ownKeys_ be _target_.[[OwnPropertyKeys]]().
38-
1. For each element _key_ of _ownKeys_, do
39-
1. Let _desc_ be ? _target_.[[GetOwnProperty]](_key_).
40-
1. If _desc_ is not *undefined*, and either _enumerable_ is *"all"* or _enumerable_ is _desc_.[[Enumerable]], then
41-
1. If _key_ is a Symbol and _keyTypes_ contains *"symbol"*, increment _count_ by 1.
42-
1. Else if _key_ is an array index and _keyTypes_ contains *"index"*, increment _count_ by 1.
43-
1. Else if _keyTypes_ contains *"nonIndexString"*, increment _count_ by 1.
44-
1. Return _count_.
45-
</emu-alg>
46-
</emu-clause>
47-
</emu-clause>
48-
</emu-clause>
12+
<emu-clause id="sec-demo-clause">
13+
<h1>Object.propertyCount ( _target_ [ , _options_ ] )</h1>
14+
<p>When the `Object.propertyCount` method is called, the following steps are taken:</p>
15+
<emu-alg>
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 CreateArrayFromList(« *"string"* »).
21+
1. Else, perform the following,
22+
1. If _keyTypes_ is not an Object, throw a TypeError exception.
23+
1. Set _keyTypes_ to ? CreateListFromArrayLike(_keyTypes_, ~all~).
24+
1. If _keyTypes_ contains any value other than *"string"*, 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 _target_.[[OwnPropertyKeys]]().
30+
1. For each element _key_ of _ownKeys_, perform the following steps, do
31+
1. Let _desc_ be _target_.[[GetOwnProperty]](_key_).
32+
1. If _desc_ is not *undefined*, then
33+
1. If _enumerable_ is not *"all"*, then
34+
1. If _enumerable_ is not equal to _desc_.[[Enumerable]], continue to the next _key_.
35+
1. If _key_ is a Symbol and _keyTypes_ contains *"symbol"*, increment _count_ by 1.
36+
1. If _key_ is a String and _keyTypes_ contains *"string"*, increment _count_ by 1.
37+
1. Return 𝔽(_count_).
38+
</emu-alg>
4939
</emu-clause>
5040

5141
<!-- Copied from ECMA-402 GetOptionsObject -->

0 commit comments

Comments
 (0)