Skip to content

Commit

Permalink
docs(pass-style): Polish and conslidate property enumeration tables (…
Browse files Browse the repository at this point in the history
…merge #1869)
  • Loading branch information
kriskowal authored Dec 1, 2023
2 parents 9063c47 + be51fb1 commit f7979e4
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 39 deletions.
28 changes: 10 additions & 18 deletions packages/pass-style/doc/copyArray-guarantees.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,16 @@ The input validation check `assertCopyArray(arr)` asserts that `passStyleOf(arr)

# How do I enumerate thee, let me list the ways

Why these properties restrictions? JavaScript has a tremendous number of different constructs for enumerating the properties of an object, with different semantics of what subset they choose to enumerate.

| API | inherited? | non-enumerable? | strings? | symbols? | output |
| --------------------------- | ----------- | --------------- | -------- | -------- | --------- |
| for..in | yes | no | yes | no | k* |
| O.keys | no | no | yes | no | [k,*] |
| O.values | no | no | yes | no | [v,*] |
| O.entries | no | no | yes | no | [[k,v],*] |
| {...obj} | no | no | yes | yes | {k:v,*} |
| O.assign after 1st arg | no | no | yes | yes | {k:v,*} |
| Reflect.ownKeys | no | yes | yes | yes | [k,*] |
| O.getOwnPropertyNames | no | yes | yes | no | [k,*] |
| O.getOwnPropertySymbols | no | yes | no | yes | [k,*] |
| O.getOwnPropertyDescriptors | no | yes | yes | yes | {k:d,*} |


Once an object passes `assertCopyArray(arr)`, all of these are guaranteed to agree except for `length`.
Since an array's `length` property is a non-enumerable string-named property, `Reflect.ownKeys`, `Object.getOwnPropertyNames`, `Object.getOwnPropertyDescriptors` will see the `length` property. The others will not.
Why these properties restrictions?
JavaScript has a [tremendous number of different constructs for enumerating the
properties](enumerating-properties.md) of an object, with different semantics
of what subset they choose to enumerate.
Once an object passes `assertCopyArray(arr)`, all of these are guaranteed to
agree except for `length`.
Since an array's `length` property is a non-enumerable string-named property,
`Reflect.ownKeys`, `Object.getOwnPropertyNames`,
`Object.getOwnPropertyDescriptors` will see the `length` property. The others
will not.

# Like Tuples from Records & Tuples.

Expand Down
25 changes: 4 additions & 21 deletions packages/pass-style/doc/copyRecord-guarantees.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,10 @@ If `r` is a proxy, then, if this plan goes as we expect, this test will throw wi

# How do I enumerate thee, let me list the ways

Why only string-named own enumerable data properties? JavaScript has a tremendous number of different constructs for enumerating the properties of an object, with different semantics of what subset they choose to enumerate:

| API | inherited? | non-enumerable? | strings? | symbols? | output |
| --------------------------- | ----------- | --------------- | -------- | -------- | --------- |
| for..in | yes | no | yes | no | k* |
| O.keys | no | no | yes | no | [k,*] |
| O.values | no | no | yes | no | [v,*] |
| O.entries | no | no | yes | no | [[k,v],*] |
| {...obj} | no | no | yes | yes | {k:v,*} |
| O.assign after 1st arg | no | no | yes | yes | {k:v,*} |
| Reflect.ownKeys | no | yes | yes | yes | [k,*] |
| O.getOwnPropertyNames | no | yes | yes | no | [k,*] |
| O.getOwnPropertySymbols | no | yes | no | yes | [k,*] |
| O.getOwnPropertyDescriptors | no | yes | yes | yes | {k:d,*} |

For accessor properties,
when the output contains a `v`, the getter is called to get the value:
O.values, O.entries, {...obj}, O.assign
when the output contains a `d`, the getter and setter are in the descriptor:
O.getOwnPropertyDescriptors

Why only string-named own enumerable data properties?
JavaScript has a [tremendous number of different constructs for enumerating the
properties](enumerating-properties.md) of an object, with different semantics
of what subset they choose to enumerate.
Once an object passes `assertRecord(r)`, all of these are guaranteed to agree.

# Like Records from Records & Tuples.
Expand Down
46 changes: 46 additions & 0 deletions packages/pass-style/doc/enumerating-properties.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

# How do I enumerate JavaScript object properties? Let me count the ways.

JavaScript has a tremendous number of different constructs for enumerating the
properties of an object, with different semantics of what subset they choose to
enumerate:

| Operation | P | NE | STR | SYM | output |
| ---------------------------------- | - | -- | --- | --- | --------- |
| `for (... in ...)` | P | | STR | | k* |
| `Object.keys` | | | STR | | [k*] |
| `Object.values` | | | STR | | [v*] |
| `Object.entries` | | | STR | | [[k,v]*] |
| `{...obj}` | | | STR | SYM | {k:v*} |
| `Object.assign` | | | STR | SYM | {k:v*} |
| `Reflect.ownKeys` | | NE | STR | SYM | [k*] |
| `Object.getOwnPropertyNames` | | NE | STR | | [k*] |
| `Object.getOwnPropertySymbols` | | NE | | SYM | [k*] |
| `Object.getOwnPropertyDescriptors` | | NE | STR | SYM | {k:d*} |

Legend:

* **P**: Includes properties inherited from up the prototype chain. For
example, `{__proto__: {a: 10}, b: 20}` has properties `"a"` and `"b"`.
* **NE**: Includes properties that are non-enumerable, like the length of an
array or methods on classes.
* **STR**: Includes properties named by a string.
* **SYM**: Includes properties named by a symbol. For example,
`{[Symbol.toStringTag]: 'inline'}` has a symbol property.
* **`k*`** indicates keys.
* **`[k*]`** indicates an array of keys.
* **`[v*]`** indicates an array of values.
* **`{k:v*}`** indicates an object mapping keys to values.
* **`{k:d*}`** indicates an object mapping keys to property descriptors.

For objects with accessor properties (getters), JavaScript has two modes of
copying, copy-by-value and copy-by-descriptor.

* Output that contains a `v` indicates that the operation copied the value.
The operation will synchronously capture a snapshot of the current
value by calling the getter.
`Object.values`, `Object.entries`, `{...object}`, and `Objeect.assign`
call getter functions and copy-by-value.
* When the output contains a `d`, the getter and setter are in the descriptor,
as in `Object.getOwnPropertyDescriptors`.
The operation does not call the getter and merely copies the descriptor.

0 comments on commit f7979e4

Please sign in to comment.