diff --git a/packages/pass-style/doc/copyArray-guarantees.md b/packages/pass-style/doc/copyArray-guarantees.md index a9fbaa8c76..5a48170404 100644 --- a/packages/pass-style/doc/copyArray-guarantees.md +++ b/packages/pass-style/doc/copyArray-guarantees.md @@ -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. diff --git a/packages/pass-style/doc/copyRecord-guarantees.md b/packages/pass-style/doc/copyRecord-guarantees.md index d0ea9067ad..6c53839865 100644 --- a/packages/pass-style/doc/copyRecord-guarantees.md +++ b/packages/pass-style/doc/copyRecord-guarantees.md @@ -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. diff --git a/packages/pass-style/doc/enumerating-properties.md b/packages/pass-style/doc/enumerating-properties.md new file mode 100644 index 0000000000..55fb2685fa --- /dev/null +++ b/packages/pass-style/doc/enumerating-properties.md @@ -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.