├── .github └── workflows │ ├── build.yml │ └── deploy.yml ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── package.json └── spec.emu /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build spec 2 | 3 | on: [pull_request, push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: ljharb/actions/node/install@main 12 | name: 'nvm install lts/* && npm install' 13 | with: 14 | node-version: lts/* 15 | - run: npm run build 16 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy gh-pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: ljharb/actions/node/install@main 15 | name: 'nvm install lts/* && npm install' 16 | with: 17 | node-version: lts/* 18 | - run: npm run build 19 | - uses: JamesIves/github-pages-deploy-action@v4 20 | with: 21 | branch: gh-pages 22 | folder: build 23 | clean: true 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # Only apps should have lockfiles 40 | yarn.lock 41 | package-lock.json 42 | npm-shrinkwrap.json 43 | pnpm-lock.yaml 44 | 45 | # Build directory 46 | build 47 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Ruben Bridgewater 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ECMAScript Proposal: `Object.propertyCount` 2 | 3 | ## Status 4 | 5 | Champion: Ruben Bridgewater, Jordan Harband 6 | 7 | Author: Ruben Bridgewater 8 | 9 | Stage: 1 10 | 11 | ## Overview 12 | 13 | This proposal introduces `Object.propertyCount`, a built-in method to efficiently and intuitively obtain the count of an object's own properties, with support for distinguishing among indexed properties, string-keyed properties, symbol properties, enumerable and non-enumerable properties, without the performance overhead of intermediate array allocations of the object's keys. 14 | 15 | ## Motivation 16 | 17 | Developers frequently rely on patterns like: 18 | 19 | ```js 20 | const obj = { a: 1, b: 2 }; 21 | const count = Object.keys(obj).length; 22 | ``` 23 | 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. 25 | 26 | For instance, React often counts props or state keys: 27 | 28 | ```js 29 | // React component example 30 | const propCount = Object.keys(this.props).length; 31 | ``` 32 | 33 | Replacing these patterns with a native and optimized counting method significantly reduces memory overhead, garbage collection, and as such, runtime performance impacts. 34 | 35 | ### Concrete usage examples 36 | 37 | I only searched for `Object.keys().length`, since that is the most common one. 38 | 39 | #### Angular 40 | 41 | - https://github.com/angular/angular/blob/7499b74d7d2d6db132d1b19a73e13cf6e306e41e/packages/router/src/url_tree.ts#L119 42 | - https://github.com/angular/angular/blob/7499b74d7d2d6db132d1b19a73e13cf6e306e41e/packages/core/src/transfer_state.ts#L120 43 | - https://github.com/angular/angular/blob/7499b74d7d2d6db132d1b19a73e13cf6e306e41e/packages/compiler/src/render3/view/i18n/util.ts#L57 44 | - https://github.com/angular/angular/blob/7499b74d7d2d6db132d1b19a73e13cf6e306e41e/packages/core/src/util/ng_dev_mode.ts#L97 45 | 46 | And multiple more. 47 | 48 | #### React 49 | 50 | - https://github.com/facebook/react/blob/254dc4d9f37eb512d4ee8bad6a0fae7ae491caef/packages/shared/shallowEqual.js#L33C9-L35 51 | - https://github.com/facebook/react/blob/254dc4d9f37eb512d4ee8bad6a0fae7ae491caef/packages/react-devtools-shared/src/hydration.js#L97 52 | - https://github.com/facebook/react/blob/254dc4d9f37eb512d4ee8bad6a0fae7ae491caef/packages/react-reconciler/src/ReactFiberHydrationDiffs.js#L416-L417 53 | - https://github.com/facebook/react/blob/254dc4d9f37eb512d4ee8bad6a0fae7ae491caef/packages/react-reconciler/src/ReactFiber.js#L677 54 | - https://github.com/facebook/react/blob/254dc4d9f37eb512d4ee8bad6a0fae7ae491caef/packages/react-server/src/ReactFizzServer.js#L2384 55 | - https://github.com/facebook/react/blob/254dc4d9f37eb512d4ee8bad6a0fae7ae491caef/packages/react-dom-bindings/src/client/ReactDOMComponent.js#L3138 56 | 57 | #### Node.js 58 | 59 | - https://github.com/nodejs/node/blob/c3b6f949748b49ef25b0239bd4582d29976fdbad/lib/internal/util/comparisons.js#L385 60 | - https://github.com/nodejs/node/blob/c3b6f949748b49ef25b0239bd4582d29976fdbad/lib/internal/util/comparisons.js#L754-L763 61 | - https://github.com/nodejs/node/blob/c3b6f949748b49ef25b0239bd4582d29976fdbad/lib/internal/util/comparisons.js#L713-L720 (could be rewritten in a more performant way with the new API) 62 | - https://github.com/nodejs/node/blob/c3b6f949748b49ef25b0239bd4582d29976fdbad/lib/internal/debugger/inspect_client.js#L248 63 | - https://github.com/nodejs/node/blob/c3b6f949748b49ef25b0239bd4582d29976fdbad/lib/internal/cluster/primary.js#L146 64 | - https://github.com/nodejs/node/blob/c3b6f949748b49ef25b0239bd4582d29976fdbad/lib/internal/console/constructor.js#L537 65 | 66 | #### Minimatch 67 | 68 | https://github.com/isaacs/minimatch/blob/0569cd3373408f9d701d3aab187b3f43a24a0db7/src/index.ts#L158 69 | 70 | #### Vue 71 | 72 | - https://github.com/vuejs/core/blob/d65b25cdda4c0e7fe8b51e000ecc3696baad0492/packages/shared/src/looseEqual.ts#L36C11-L37 73 | - https://github.com/vuejs/core/blob/d65b25cdda4c0e7fe8b51e000ecc3696baad0492/rollup.config.js#L251 74 | - https://github.com/vuejs/core/blob/d65b25cdda4c0e7fe8b51e000ecc3696baad0492/packages/compiler-core/src/utils.ts#L505 75 | - https://github.com/vuejs/core/blob/d65b25cdda4c0e7fe8b51e000ecc3696baad0492/packages/runtime-core/src/customFormatter.ts#L123 76 | - https://github.com/vuejs/core/blob/d65b25cdda4c0e7fe8b51e000ecc3696baad0492/packages/compiler-sfc/src/style/pluginScoped.ts#L33 77 | - https://github.com/vuejs/core/blob/d65b25cdda4c0e7fe8b51e000ecc3696baad0492/packages/compiler-core/src/transforms/transformElement.ts#L905 78 | 79 | #### Lodash 80 | 81 | Lodash uses an own implementation that behaves as Object.keys() 82 | 83 | - https://github.com/lodash/lodash/blob/8a26eb42adb303f4adc7ef56e300f14c5992aa68/dist/lodash.js#L9921 84 | - https://github.com/lodash/lodash/blob/8a26eb42adb303f4adc7ef56e300f14c5992aa68/dist/lodash.js#L11561 85 | 86 | #### Other popular ones 87 | 88 | Almost all popular JS/TS modules make use of this pattern. 89 | 90 | - https://github.com/trekhleb/javascript-algorithms/blob/e40a67b5d1aaf006622a90e2bda60043f4f66679/src/algorithms/graph/detect-cycle/detectDirectedCycle.js#L83 91 | - https://github.com/storybookjs/storybook/blob/b91e25a25c8c1cc77ea6b316d03b4cce183d815c/code/core/src/theming/ensure.ts#L15 92 | - https://github.com/storybookjs/storybook/blob/b91e25a25c8c1cc77ea6b316d03b4cce183d815c/code/core/src/theming/ensure.ts#L15 93 | - https://github.com/storybookjs/storybook/blob/b91e25a25c8c1cc77ea6b316d03b4cce183d815c/code/lib/blocks/src/blocks/Controls.tsx#L60-L63 94 | - https://github.com/storybookjs/storybook/blob/b91e25a25c8c1cc77ea6b316d03b4cce183d815c/scripts/sandbox/templates/root.ejs#L7 95 | - https://github.com/storybookjs/storybook/blob/b91e25a25c8c1cc77ea6b316d03b4cce183d815c/code/lib/blocks/src/blocks/DocsPage.tsx#L15 96 | - https://github.com/storybookjs/storybook/blob/b91e25a25c8c1cc77ea6b316d03b4cce183d815c/code/core/assets/server/template.ejs#L67 97 | - https://github.com/tailwindlabs/tailwindcss/blob/e8715d081eac683d002892b8b3e13550f0276b45/packages/tailwindcss/src/compat/theme-variants.ts#L9 98 | - https://github.com/tailwindlabs/tailwindcss/blob/e8715d081eac683d002892b8b3e13550f0276b45/packages/%40tailwindcss-upgrade/src/migrate-postcss.ts#L346 99 | - https://github.com/tailwindlabs/tailwindcss/blob/e8715d081eac683d002892b8b3e13550f0276b45/packages/tailwindcss/src/compat/apply-compat-hooks.ts#L100 100 | - https://github.com/puppeteer/puppeteer/blob/ff74c58464f985253b0a986f5fbbe4edc1658a42/packages/puppeteer-core/src/bidi/HTTPRequest.ts#L149 101 | - https://github.com/puppeteer/puppeteer/blob/ff74c58464f985253b0a986f5fbbe4edc1658a42/packages/puppeteer-core/src/bidi/Page.ts#L623 102 | - https://github.com/excalidraw/excalidraw/blob/e1bb59fb8f115cd8e75fcaaeefa03a81b0fdc697/packages/excalidraw/actions/actionSelectAll.ts#L50 103 | - https://github.com/excalidraw/excalidraw/blob/e1bb59fb8f115cd8e75fcaaeefa03a81b0fdc697/packages/excalidraw/change.ts#L133 104 | - https://github.com/excalidraw/excalidraw/blob/e1bb59fb8f115cd8e75fcaaeefa03a81b0fdc697/packages/excalidraw/groups.ts#L38 105 | - https://github.com/excalidraw/excalidraw/blob/e1bb59fb8f115cd8e75fcaaeefa03a81b0fdc697/packages/excalidraw/components/App.tsx#L2760 106 | - https://github.com/excalidraw/excalidraw/blob/e1bb59fb8f115cd8e75fcaaeefa03a81b0fdc697/packages/excalidraw/actions/actionProperties.tsx#L1038 107 | - https://github.com/excalidraw/excalidraw/blob/e1bb59fb8f115cd8e75fcaaeefa03a81b0fdc697/packages/excalidraw/clipboard.ts#L317 108 | - https://github.com/excalidraw/excalidraw/blob/e1bb59fb8f115cd8e75fcaaeefa03a81b0fdc697/packages/excalidraw/element/mutateElement.ts#L44 109 | - https://github.com/microsoft/vscode/blob/2e6728cc3b6ab7f2bc5223dd52abb5f3b595b827/src/vs/base/common/equals.ts#L87-L91 110 | - https://github.com/microsoft/vscode/blob/2e6728cc3b6ab7f2bc5223dd52abb5f3b595b827/src/vs/platform/policy/common/policy.ts#L37 111 | - https://github.com/microsoft/vscode/blob/2e6728cc3b6ab7f2bc5223dd52abb5f3b595b827/build/lib/util.ts#L37 112 | - https://github.com/microsoft/vscode/blob/2e6728cc3b6ab7f2bc5223dd52abb5f3b595b827/src/vs/platform/policy/node/nativePolicyService.ts#L26 113 | - https://github.com/microsoft/vscode/blob/2e6728cc3b6ab7f2bc5223dd52abb5f3b595b827/extensions/terminal-suggest/src/fig/shared/utils.ts#L165 114 | - https://github.com/microsoft/vscode/blob/2e6728cc3b6ab7f2bc5223dd52abb5f3b595b827/build/lib/i18n.ts#L90 115 | - https://github.com/microsoft/vscode/blob/2e6728cc3b6ab7f2bc5223dd52abb5f3b595b827/src/vs/platform/product/common/product.ts#L61 116 | - https://github.com/sveltejs/svelte/blob/f498a21063894e6e515e62d753396410624b2e0f/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js#L259 117 | - https://github.com/mrdoob/three.js/blob/b0805c2a0fd46c137d605ba098bc2b17d507b46f/src/materials/ShaderMaterial.js#L359 118 | - https://github.com/mrdoob/three.js/blob/b0805c2a0fd46c137d605ba098bc2b17d507b46f/editor/js/Sidebar.Geometry.BufferGeometry.js#L60 119 | - https://github.com/mrdoob/three.js/blob/b0805c2a0fd46c137d605ba098bc2b17d507b46f/examples/jsm/loaders/3MFLoader.js#L239 120 | - https://github.com/vercel/next.js/blob/5c5875105af06e17ccad4080a6ace137f14cdabb/packages/next/src/build/babel/loader/get-config.ts#L177 121 | - https://github.com/vercel/next.js/blob/5c5875105af06e17ccad4080a6ace137f14cdabb/turbopack/packages/devlow-bench/src/cli.ts#L34 122 | - https://github.com/vercel/next.js/blob/5c5875105af06e17ccad4080a6ace137f14cdabb/packages/next/check-error-codes.js#L31 123 | - https://github.com/vercel/next.js/blob/5c5875105af06e17ccad4080a6ace137f14cdabb/scripts/trace-dd.mjs#L67 124 | - https://github.com/vercel/next.js/blob/5c5875105af06e17ccad4080a6ace137f14cdabb/packages/next/src/server/lib/utils.ts#L243 125 | - https://github.com/vercel/next.js/blob/5c5875105af06e17ccad4080a6ace137f14cdabb/scripts/trace-to-tree.mjs#L147 126 | 127 | ## Problem Statement 128 | 129 | Currently, accurately counting object properties involves verbose and inefficient workarounds: 130 | 131 | ```js 132 | const count = [ 133 | ...Object.getOwnPropertyNames(obj), 134 | ...Object.getOwnPropertySymbols(obj) 135 | ].length; 136 | 137 | const reflectCount = Reflect.ownKeys(obj).length; 138 | 139 | assert.strictEqual(count, reflectCount); 140 | ``` 141 | 142 | This creates intermediate arrays, causing unnecessary memory usage and garbage collection, impacting application performance — especially at scale and in performance-critical code paths. 143 | 144 | On top of that, it is also not possible to identify an array that is sparse without calling `Object.keys()` (or similar). This API would allow that by explicitly checking for own index properties. 145 | 146 | ## Proposed API 147 | 148 | ```js 149 | Object.propertyCount(target[, options]) 150 | ``` 151 | 152 | ### Parameters 153 | 154 | - **`target`**: The object whose properties will be counted. 155 | - Throws `TypeError` if target is not an object. 156 | - **`options`** *(optional)*: An object specifying filtering criteria: 157 | - `keyTypes`: Array specifying property types to include: 158 | - Possible values: `'string'`, `'symbol'`, and `'all'`. 159 | - Defaults to `'string'` (aligning closely with `Object.keys`). 160 | - Throws `TypeError` if provided invalid values. 161 | - `enumerable`: Indicates property enumerability: 162 | - `true` to count only enumerable properties (default). 163 | - `false` to count only non-enumerable properties. 164 | - `'all'` to count both enumerable and non-enumerable properties. 165 | - Throws `TypeError` if provided invalid values. 166 | 167 | Defaults align closely with `Object.keys` for ease of adoption, ensuring intuitive behavior without needing explicit configuration in common cases. 168 | 169 | The naming of keyTypes and if it's an array or an object or the like is open for discussion. 170 | 171 | Similar applies to the enumerable option: true, false, and `'all'` seems cleanest, but it's not important how they are named. 172 | 173 | ## Detailed Examples and Edge Cases 174 | 175 | - **Empty object**: 176 | 177 | ```js 178 | Object.propertyCount({}); // returns 0 179 | ``` 180 | 181 | - **Object without prototype**: 182 | 183 | ```js 184 | const obj = Object.create(null); 185 | obj.property = 1; 186 | Object.propertyCount(obj); // returns 1 187 | ``` 188 | 189 | ```js 190 | const obj2 = { __proto__: null }); 191 | obj2.property = 1; 192 | Object.propertyCount(obj2); // returns 1 193 | ``` 194 | 195 | - **Array index keys**: 196 | 197 | See https://tc39.es/ecma262/#array-index 198 | 199 | ```js 200 | let obj = { "01": "string key", 1: "index", 2: "index" }; 201 | Object.propertyCount(obj, { keyTypes: ['string'] }); // returns 3 202 | 203 | obj = { "0": "index", "-1": "string key", "01": "string key" }; 204 | Object.propertyCount(obj, { keyTypes: ['string'] }); // returns 3 205 | ``` 206 | 207 | - **String based keys**: 208 | 209 | ```js 210 | const obj = { "01": "string key", 1: "index", 2: "index" }; 211 | Object.propertyCount(obj, { keyTypes: ['string'] }); // returns 3 212 | ``` 213 | 214 | - **Symbol based keys**: 215 | 216 | ```js 217 | const obj = { [Symbol()]: "symbol", 1: "index", 2: "index" }; 218 | Object.propertyCount(obj, { keyTypes: ['symbol'] }); // returns 2 219 | ``` 220 | 221 | ## Explicit Semantics 222 | 223 | - Only own properties are considered. 224 | - Enumerability explicitly defined by the `enumerable` parameter. 225 | - Avoids intermediate array allocation entirely when implemented natively. 226 | 227 | ## Algorithmic Specification 228 | 229 | The native implementation should strictly avoid creating intermediate arrays or unnecessary allocations: 230 | 231 | 1. Initialize a numeric property counter to `0`. 232 | 2. Iterate directly over the object's own property descriptors 233 | - Access the internal property keys directly via the object's internal slots. 234 | - For each own property: 235 | - Determine if the key is a string or a symbol. 236 | - Check if the property type matches any specified in `keyTypes`. 237 | - If `enumerable` is not `'all'`, match the property's enumerability against the provided boolean value. 238 | - If the property meets all criteria, increment the counter. 239 | 3. Return the final count value. 240 | 241 | See the [spec proposal](./spec.emu) for details. 242 | 243 | ## Alternatives Considered 244 | 245 | - **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. 247 | 248 | ## TC39 Stages and Champion 249 | 250 | - Ready for **Stage 2** 251 | 252 | ## Use Cases 253 | 254 | - Improved readability and explicit intent 255 | - Significant **performance** gains 256 | - **Reduced memory** overhead 257 | - **Simpler code** 258 | 259 | ## Precedent 260 | 261 | 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. 262 | 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 | 265 | ## Polyfill 266 | 267 | ```js 268 | const validTypes = new Set(['string', 'symbol']); 269 | 270 | Object.propertyCount = function propertyCount(target, options) { 271 | if (typeof target !== 'object' || target === null) { 272 | throw new TypeError(`Expected target to be an object. Received ${typeof target}`); 273 | } 274 | 275 | if (typeof options === 'undefined') { 276 | return Object.keys(target).length; 277 | } 278 | 279 | const { keyTypes = ['string'], enumerable = true } = options || {}; 280 | 281 | for (const type of keyTypes) { 282 | if (!validTypes.has(type)) { 283 | throw new TypeError(`Invalid property type (${type}) in 'keyTypes' option.`); 284 | } 285 | } 286 | 287 | if (typeof enumerable !== 'boolean' && enumerable !== 'all') { 288 | throw new TypeError(`Invalid input (${enumerable}) in 'enumerable' option.`); 289 | } 290 | 291 | let props = []; 292 | 293 | if (keyTypes.includes('string')) { 294 | props = enumerable === true ? Object.keys(target) : Object.getOwnPropertyNames(target); 295 | } 296 | 297 | if (keyTypes.includes('symbol')) { 298 | props = props.concat(Object.getOwnPropertySymbols(target)); 299 | } 300 | 301 | if (enumerable !== 'all') { 302 | props = props.filter(key => Object.getOwnPropertyDescriptor(target, key).enumerable === enumerable); 303 | } 304 | 305 | return props.length; 306 | }; 307 | ``` 308 | 309 | ## Considerations 310 | 311 | - **Backwards compatibility**: Fully backward compatible. 312 | - **Performance**: Native implementation will significantly outperform existing approaches by eliminating intermediate arrays. 313 | - **Flexibility**: Enumerable properties counted by default; easy inclusion/exclusion. 314 | - **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). 316 | 317 | ## Conclusion 318 | 319 | `Object.propertyCount` offers substantial performance benefits by efficiently counting object properties without intermediate arrays, enhancing ECMAScript with clarity, performance, and reduced memory overhead. 320 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "object-property-count", 4 | "description": "Proposal for a fast way to count object properties.", 5 | "scripts": { 6 | "start": "npm run build-loose -- --watch", 7 | "build": "npm run build-loose -- --strict", 8 | "build-loose": "node -e 'fs.mkdirSync(\"build\", { recursive: true })' && ecmarkup --load-biblio @tc39/ecma262-biblio --verbose spec.emu build/index.html --lint-spec" 9 | }, 10 | "homepage": "https://github.com/BridgeAR/object-property-count#readme", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/BridgeAR/object-property-count.git" 14 | }, 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@tc39/ecma262-biblio": "^2.1.2895", 18 | "ecmarkup": "^21.3.0" 19 | }, 20 | "engines": { 21 | "node": ">= 18" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /spec.emu: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
 7 | title: Object.propertyCount
 8 | stage: 1
 9 | contributors: Ruben Bridgewater, Jordan Harband
10 | 
11 | 12 | 13 |

Object.propertyCount ( _target_ [ , _options_ ] )

14 |

When the `Object.propertyCount` method is called, the following steps are taken:

15 | 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 | 39 |
40 | 41 | 42 | 43 | 44 |

45 | GetOptionsObject ( 46 | _options_: an ECMAScript language value, 47 | ): either a normal completion containing an Object or a throw completion 48 |

49 |
50 |
description
51 |
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 |
55 |
56 | 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. 62 | 63 |
64 | --------------------------------------------------------------------------------