├── .npmrc
├── LICENSE
├── README.md
├── index.html
├── package.json
└── spec.emu
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 ECMA TC39 and contributors
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Collection {coerceKey, coerceValue}
2 |
3 | This proposal seeks to add a `coerceKey` and `coerceValue` parameter to collection creation.
4 |
5 | [Rendered Spec](https://tc39.github.io/proposal-collection-normalization/)
6 |
7 | ## Motivation
8 |
9 | Various data structures are expected to have consistent constraints and or normalization applied to their keys and/or values when being used after creation. In order to facilitate normalization be guaranteed for such data types it needs to be applied prior to being inserted into the data structure. The current workflow is to manually ensure that at all locations which insert data into the structure properly normalize the data.
10 |
11 | This leads to various issues such as potentially fragile boilerplate problems such as; normalization not applied to all insertion locations or normalization not applied in consistent manners across insertion locations.
12 |
13 | This proposal does add a new capability of guarantees around the stored values in these data structures. Currently calling various built ins methods can avoid any mechanism to intercept and normalize data as it is inserted by calling methods on a structure like `Map.prototype.set.call(mapOfStringsToStrings, number)` which prevents guarantees about a `Map` having a consistent set of guarantees about the data it contains.
14 |
15 | Various examples of structures that normalize data as it is inserted exist in the wild:
16 |
17 | * [`URLSearchParams`](https://url.spec.whatwg.org/#urlsearchparams)
18 |
19 | Converts data to strings for both parameter name and value.
20 |
21 | * [`DomStringMap`](https://html.spec.whatwg.org/#domstringmap)
22 |
23 | Validates and converts data to strings for both header name and value.
24 |
25 | * [`Header`](https://fetch.spec.whatwg.org/#headers-class)
26 |
27 | Converts data to strings for both header name and value.
28 |
29 | * [`HTMLOptionsCollection[[setter]]`](https://html.spec.whatwg.org/#the-htmloptionscollection-interface)
30 |
31 | Clamps the key when appending to `length + 1` by using `append`.
32 |
33 | * [`Attributes.setNamedItem*`](https://dom.spec.whatwg.org/#concept-element-attributes-set)
34 |
35 | Performs validation on key.
36 |
37 | All of these do similar effects using differing ways of applying data type coercion and validation of data being inserted.
38 |
39 | In order to unify how to define these general operations on dealing with incoming data and to avoid boilerplate issues and fragile base class issues as mentioned in the FAQ a hook to
40 | intercept data would alleviate some issues.
41 |
42 | ## Use cases
43 |
44 | ### Specialized maps
45 |
46 | Given an application with User Objects it may be desirable to create collections based upon username and email for separate purposes.
47 |
48 | ```mjs
49 | new Map(undefined, {
50 | coerceKey({email}) {
51 | return email;
52 | },
53 | coerceValue(state) {
54 | return state instanceof AccountState ?
55 | state :
56 | new AccountState(state);
57 | }
58 | });
59 | ```
60 |
61 | ```mjs
62 | new Set(undefined, {
63 | coerceValue({username}) {
64 | return username;
65 | }
66 | });
67 | ```
68 |
69 | ### Checked keys
70 |
71 | It is a common occurrence to want to check types when performing operations on collections. This can be done during keying.
72 |
73 | ```mjs
74 | new Map(undefined, {
75 | coerceKey(user) {
76 | if (user instanceof User !== true) {
77 | throw new TypeError('Expected User for key');
78 | }
79 | return user;
80 | }
81 | });
82 | ```
83 |
84 | ### DOM Example: [Headers..set](https://fetch.spec.whatwg.org/#dom-headers-set)
85 |
86 | > 1. Normalize value.
87 |
88 | > 2. If name is not a name or value is not a value, then throw a TypeError.
89 |
90 | > 3. If this’s guard is "immutable", then throw a TypeError.
91 |
92 | > 4. Otherwise, if this’s guard is "request" and name is a forbidden header name, return.
93 |
94 | > 5. Otherwise, if this’s guard is "request-no-cors" and name/value is not a no-CORS-safelisted request-header, return.
95 |
96 | > 6. Otherwise, if this’s guard is "response" and name is a forbidden response-header name, return.
97 |
98 | > 7. Set name/value in this’s header list.
99 |
100 | > 8. If this’s guard is "request-no-cors", then remove privileged no-CORS request headers from this.
101 |
102 | All of these can be interacted with if a hook can intercept `name` and `value` as it comes in. The general workflow is to store a `name` and `value` in the backing `header list` storage.
103 |
104 | ### DOM Example: [SearchParams..has](https://url.spec.whatwg.org/#dom-urlsearchparams-has)
105 |
106 | > The has(name) method steps are to return true if there is a name-value pair whose name is name in this’s list, and false otherwise.
107 |
108 | Note, all normalization is done in WebIDL layer here to convert `name` to a USVString, there is no actual text stating that normalization is done for `name`.
109 |
110 | ## FAQ
111 |
112 | ### How do other languages handle this customized keying?
113 |
114 | A collection of references can be found via [this document](https://docs.google.com/document/d/1qxSLyiButKocM6ENufhvnNJcZh18nDAWcFT2HlTJahQ/edit#).
115 |
116 | Generally it falls into using container types. If you wanted to create a `Map` of `People` by `person.email`. You would implement a wrapper class `PersonByEmail` to use as your key, and others for keying of other aspects. Static typing and compiler/language enforced coercion can alleviate problems with misusing collections, but wrapping and unwrapping is manual in scenarios with dynamic typing that cannot be coerced automatically.
117 |
118 | This proposal would provide a hook to do that manual wrapping and unwrapping without requiring the user of a collection to remain vigilant about properly marshalling keys before providing them to the collection.
119 |
120 | ### When are the normalization steps applied?
121 |
122 | Normalization is applied when data is incoming to find the identity of the key location in `[[MapData]]` and when placing the value in `[[SetData]]` or `[[MapData]]`. e.g.
123 |
124 | ```mjs
125 | const map = new Map([], {
126 | coerceKey: String
127 | });
128 | // stored using { [[Key]]: "1", [[Value]]: "one" } in map.[[MapData]]
129 | map.set(1, 'one');
130 | // looks for corresponding { [[Key]]: "1" } in map.[[MapData]]
131 | map.has(1); // true
132 | // functions directly exposing the underlying entry list are unaffected
133 | [...map.entries()]; // [["1", "one"]]
134 |
135 | const set = new Set([], {coerceValue: JSON.stringify});
136 | // stored using { [[Value]]: '{"path": "/foo"}' } in set.[[SetData]]
137 | set.add({path: '/foo'});
138 | // looks for corresponding { [[Value]]: '{"path": "/foo"}' } in set.[[SetData]]
139 | set.has({path: '/foo')};
140 | // functions directly exposing the underlying entry list are unaffected
141 | [...set]; // ['{"path": "/foo"}']
142 | ```
143 |
144 | Normalization is not done when iterating or returning internal data, it is only done on parameters.
145 |
146 | ### Why would someone want to use `coerceValue` with Map?
147 |
148 | A variety of use cases exist to normalize the values of map like structures in different APIs. Even if they do not directly use Map, we can se the utility of this pattern from existing DOM APIs.
149 |
150 | * `URLSearchParams`
151 | * `DomStringMap`
152 | * `Header`
153 |
154 | Node also has APIs that also normalize values such as `process.env`.
155 |
156 | Normalizing values can avoid certain situations as well such as putting invalid values into a Map by either validation errors, coercion, or other means. Existing map like structures such as `require.cache` can produce error if you put the incorrect values in them. A normalization step allows the map to properly handle situations when unexpected values are inserted.
157 |
158 | ### Why are Sets only given `coerceValue`?
159 |
160 | Sets are collections of values, and do not have a mapping operation from one value to another.
161 |
162 | #### Why not call it `coerceKey` for Sets?
163 |
164 | An audit of other languages was done with their documentation and APIs concerning Sets. The majority of terminology used was "elements"; however, the terms "keys" and "values" were also used. It was noted that whenever "keys" was used "values" was also used, but when "values" was used it did not always also use "keys". To match existing JS usage of terms "values" was chosen as the base for this name.
165 |
166 | ### Why not `value[Symbol.coerceKey]`?
167 |
168 | Having specialized identity conflicts with the idea of having multiple kinds of specialized maps per type of value. It also would cause conflicts when wanting to specialize keys that are based upon primitives.
169 |
170 | ### Why not encourage extending collections?
171 |
172 | 1. This would be succeptible to prototype crawling such as:
173 |
174 | ```mjs
175 | myCustomMap.__proto__.get.call(myCustomMap, key);
176 | ```
177 |
178 | which would somewhat invalidate the idea of checking types of keys.
179 |
180 | 2. It prevents needing to synchronize all of the methods which is a fair amount of boilerplate and potential place for code going out of sync. It also means that your custom implementation will work even if new methods are added to collections in the JS standard library:
181 |
182 | ```mjs
183 | class MyMap extends Map {
184 | constructor([...entries]) {
185 | super(entries.map(...));
186 | }
187 | delete(k) { ... }
188 | get(k) { ... }
189 | has(k) { ... }
190 | set(k, v) { ... }
191 | }
192 | ```
193 |
194 | If we add something like `emplace()` this code now needs to be updated or it will have bugs if people expect it to work like a standard Map.
195 |
196 | This is roughly [the fragile base class problem](https://en.wikipedia.org/wiki/Fragile_base_class), where `Map` is the base class.
197 |
198 | 3. Even if this is a userland solution, it seems prudent to allow easier usage of maps. We should aim to alleviate developers without requiring that all new features have new kernel semantics. I spoke of this with respect to [expanding the standard library](https://docs.google.com/presentation/d/1QSwQYJz4c1VESEKTWPqrAPbDn_y9lTBBjaWRjej1c-w/view#slide=id.p).
199 |
200 | 4. Composition, while extending is nice it doesn't always allow for simple chaining and composition of features. If we introduce `RekeyableMap` as a concrete base class it may conflict with other base classes that may be introduced like if there was `InsertIfMissingMap`. Since both are base classes it would not allow both features to be combined easily.
201 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
If collection does not have [[CoerceKey]] or collection.[[CoerceKey]] is undefined
Return data.
Else, return Call(collection.[[CoerceKey]], collection, « data »).
1816 |
1817 |
1818 |
1819 |
1820 |
1.2NormalizeCoerceValue( collection , data )
1821 |
If collection does not have [[CoerceValue]] or collection.[[CoerceValue]] is undefined
Return data.
Else, return Call(collection.[[CoerceValue]], collection, « data »).
1822 |
1823 |
1824 |
1825 |
1826 |
Modified algorithms
1827 |
1828 |
1829 |
2Keyed Collections
1830 |
1831 |
1832 |
2.1Map Objects
1833 |
Map objects are collections of key/value pairs where both the keys and values may be arbitrary ECMAScript language values. A distinct key value may only occur in one key/value pair within the Map's collection. Distinct key values are discriminated using the SameValueZero comparison algorithm.
1834 |
Map object must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structures used in this Map objects specification is only intended to describe the required observable semantics of Map objects. It is not intended to be a viable implementation model.
1835 |
1836 |
1837 |
2.1.1The Map Constructor
1838 |
The Map constructor:
1839 |
1840 |
is the intrinsic object %Map%.
1841 |
is the initial value of the Map property of the global object.
1842 |
creates and initializes a new Map object when called as a constructor.
1843 |
is not intended to be called as a function and will throw an exception when called in that manner.
1844 |
is designed to be subclassable. It may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified Map behaviour must include a super call to the Map constructor to create and initialize the subclass instance with the internal state necessary to support the Map.prototype built-in methods.
1845 |
1846 |
1847 |
1848 |
2.1.1.1Map ( [ iterable [, options ] ] )
1849 |
When the Map function is called with optional argument iterable, the following steps are taken:
1850 |
If NewTarget is undefined, throw a TypeError exception.
Let map be ? OrdinaryCreateFromConstructor(NewTarget, "%MapPrototype%", « [[MapData]], [[CoerceKey]], [[CoerceValue]] »).
If the parameter iterable is present, it is expected to be an object that implements an @@iterator method that returns an iterator object that produces a two element array-like object whose first element is a value that will be used as a Map key and whose second element is the value to associate with that key.
1856 |
1857 |
1858 |
1859 |
1860 |
1861 |
2.1.2Properties of the Map Prototype Object
1862 |
The Map prototype object:
1863 |
1864 |
is the intrinsic object %MapPrototype%.
1865 |
has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
1866 |
is an ordinary object.
1867 |
does not have a [[MapData]] internal slot.
1868 |
does not have a [[CoerceKey]] internal slot.
1869 |
does not have a [[CoerceValue]] internal slot.
1870 |
1871 |
1872 |
1873 |
2.1.2.1Map.prototype.delete ( key )
1874 |
The following steps are taken:
1875 |
Let M be the this value.
If Type(M) is not Object, throw a TypeError exception.
If M does not have a [[MapData]] internal slot, throw a TypeError exception.
Let normalizedKey be ? NormalizeCoerceKey(M, key).
For each Record { [[Key]], [[Value]] } p that is an element of entries, do
If p.[[Key]] is not empty and SameValueZero(p.[[Key]], normalizedKeykey) is true, then
Set p.[[Key]] to empty.
Set p.[[Value]] to empty.
Return true.
Return false.
1876 |
1877 | Note
1878 |
The value empty is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.
1879 |
1880 |
1881 |
1882 |
1883 |
2.1.2.2Map.prototype.get ( key )
1884 |
The following steps are taken:
1885 |
Let M be the this value.
If Type(M) is not Object, throw a TypeError exception.
If M does not have a [[MapData]] internal slot, throw a TypeError exception.
Let normalizedKey be ? NormalizeCoerceKey(M, key).
For each Record { [[Key]], [[Value]] } p that is an element of entries, do
If p.[[Key]] is not empty and SameValueZero(p.[[Key]], normalizedKeykey) is true, then
Set p.[[Value]] to normalizedValuevalue.
Return M.
If key is -0, set key to +0.
Let p be the Record { [[Key]]: key, [[Value]]: normalizedValuevalue }.
Append p as the last element of entries.
Return M.
1900 |
1901 |
1902 |
1903 |
1904 |
2.1.2.5Properties of Map Instances
1905 |
Map instances are ordinary objects that inherit properties from the Map prototype. Map instances also have [[MapData]], [[CoerceKey]], and [[CoerceValue]] internal slots.
1906 |
1907 |
1908 |
1909 |
1910 |
2.1.3Set Objects
1911 |
Set objects are collections of ECMAScript language values. A distinct value may only occur once as an element of a Set's collection. Distinct values are discriminated using the SameValueZero comparison algorithm.
1912 |
Set objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structures used in this Set objects specification is only intended to describe the required observable semantics of Set objects. It is not intended to be a viable implementation model.
1913 |
1914 |
1915 |
2.1.3.1The Set Constructor
1916 |
The Set constructor:
1917 |
1918 |
is the intrinsic object %Set%.
1919 |
is the initial value of the Set property of the global object.
1920 |
creates and initializes a new Set object when called as a constructor.
1921 |
is not intended to be called as a function and will throw an exception when called in that manner.
1922 |
is designed to be subclassable. It may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified Set behaviour must include a super call to the Set constructor to create and initialize the subclass instance with the internal state necessary to support the Set.prototype built-in methods.
1923 |
1924 |
1925 |
1926 |
2.1.3.1.1Set ( [ iterable [, options ]] )
1927 |
When the Set function is called with optional argument iterable, the following steps are taken:
1928 |
If NewTarget is undefined, throw a TypeError exception.
If e is not empty and SameValueZero(e, normalizedValuevalue) is true, then
Replace the element of entries whose value is e with an element whose value is empty.
Return true.
Return false.
1958 |
1959 | Note
1960 |
The value empty is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.
1961 |
1962 |
1963 |
1964 |
1965 |
2.1.3.2.3Set.prototype.has ( value )
1966 |
The following steps are taken:
1967 |
Let S be the this value.
If Type(S) is not Object, throw a TypeError exception.
If S does not have a [[SetData]] internal slot, throw a TypeError exception.
Let normalizedValue be ? NormalizeCoerceValue(S, value).
If e is not empty and SameValueZero(e, normalizedValuevalue) is true, return true.
Return false.
1968 |
1969 |
1970 |
1971 |
1972 |
1973 |
1974 |
2.1.4WeakMap Objects
1975 |
WeakMap objects are collections of key/value pairs where the keys are objects and values may be arbitrary ECMAScript language values. A WeakMap may be queried to see if it contains a key/value pair with a specific key, but no mechanism is provided for enumerating the objects it holds as keys. If an object that is being used as the key of a WeakMap key/value pair is only reachable by following a chain of references that start within that WeakMap, then that key/value pair is inaccessible and is automatically removed from the WeakMap. WeakMap implementations must detect and remove such key/value pairs and any associated resources.
1976 |
An implementation may impose an arbitrarily determined latency between the time a key/value pair of a WeakMap becomes inaccessible and the time when the key/value pair is removed from the WeakMap. If this latency was observable to ECMAScript program, it would be a source of indeterminacy that could impact program execution. For that reason, an ECMAScript implementation must not provide any means to observe a key of a WeakMap that does not require the observer to present the observed key.
1977 |
WeakMap objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of key/value pairs in the collection. The data structure used in this WeakMap objects specification are only intended to describe the required observable semantics of WeakMap objects. It is not intended to be a viable implementation model.
1978 | Note
1979 |
WeakMap and WeakSets are intended to provide mechanisms for dynamically associating state with an object in a manner that does not “leak” memory resources if, in the absence of the WeakMap or WeakSet, the object otherwise became inaccessible and subject to resource reclamation by the implementation's garbage collection mechanisms. This characteristic can be achieved by using an inverted per-object mapping of weak map instances to keys. Alternatively each weak map may internally store its key to value mappings but this approach requires coordination between the WeakMap or WeakSet implementation and the garbage collector. The following references describe mechanism that may be useful to implementations of WeakMap and WeakSets:
1980 |
Barry Hayes. 1997. Ephemerons: a new finalization mechanism. In Proceedings of the 12th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications (OOPSLA '97), A. Michael Berman (Ed.). ACM, New York, NY, USA, 176-183, http://doi.acm.org/10.1145/263698.263733.
is the initial value of the WeakMap property of the global object.
1990 |
creates and initializes a new WeakMap object when called as a constructor.
1991 |
is not intended to be called as a function and will throw an exception when called in that manner.
1992 |
is designed to be subclassable. It may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified WeakMap behaviour must include a super call to the WeakMap constructor to create and initialize the subclass instance with the internal state necessary to support the WeakMap.prototype built-in methods.
1993 |
1994 |
1995 |
1996 |
2.1.4.1.1WeakMap ( [ iterable [, options ]] )
1997 |
When the WeakMap function is called with optional argument iterable, the following steps are taken:
1998 |
If NewTarget is undefined, throw a TypeError exception.
If the parameter iterable is present, it is expected to be an object that implements an @@iterator method that returns an iterator object that produces a two element array-like object whose first element is a value that will be used as a WeakMap key and whose second element is the value to associate with that key.
2004 |
2005 |
2006 |
2007 |
2008 |
2009 |
2.1.4.2Properties of the WeakMap Prototype Object
2010 |
The WeakMap prototype object:
2011 |
2012 |
is the intrinsic object %WeakMapPrototype%.
2013 |
has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
2014 |
is an ordinary object.
2015 |
does not have a [[WeakMapData]] internal slot.
2016 |
does not have a [[CoerceKey]] internal slot.
2017 |
does not have a [[CoerceValue]] internal slot.
2018 |
2019 |
2020 |
2021 |
2.1.4.2.1WeakMap.prototype.delete ( key )
2022 |
The following steps are taken:
2023 |
Let M be the this value.
If Type(M) is not Object, throw a TypeError exception.
If M does not have a [[WeakMapData]] internal slot, throw a TypeError exception.
Let normalizedKey be ? NormalizeCoerceKey(M, key).
Let entries be the List that is M.[[WeakMapData]].
For each Record { [[Key]], [[Value]] } p that is an element of entries, do
If p.[[Key]] is not empty and SameValue(p.[[Key]], normalizedKeykey) is true, then
Set p.[[Key]] to empty.
Set p.[[Value]] to empty.
Return true.
Return false.
2024 |
2025 | Note
2026 |
The value empty is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.
2027 |
2028 |
2029 |
2030 |
2031 |
2.1.4.2.2WeakMap.prototype.get ( key )
2032 |
The following steps are taken:
2033 |
Let M be the this value.
If Type(M) is not Object, throw a TypeError exception.
If M does not have a [[WeakMapData]] internal slot, throw a TypeError exception.
Let normalizedKey be ? NormalizeCoerceKey(M, key).
Let entries be the List that is M.[[WeakMapData]].
For each Record { [[Key]], [[Value]] } p that is an element of entries, do
If p.[[Key]] is not empty and SameValue(p.[[Key]], normalizedKeykey) is true, return true.
Return false.
2041 |
2042 |
2043 |
2044 |
2045 |
2.1.4.2.4WeakMap.prototype.set ( key, value )
2046 |
The following steps are taken:
2047 |
Let M be the this value.
If Type(M) is not Object, throw a TypeError exception.
If M does not have a [[WeakMapData]] internal slot, throw a TypeError exception.
Let normalizedKey be ? NormalizeCoerceKey(M, key).
Let normalizedValue be ? NormalizeCoerceValue(M, value).
Let entries be the List that is M.[[WeakMapData]].
If Type(key) is not Object, throw a TypeError exception.
For each Record { [[Key]], [[Value]] } p that is an element of entries, do
If p.[[Key]] is not empty and SameValue(p.[[Key]], normalizedKeykey) is true, then
Set p.[[Value]] to value.
Return M.
Let p be the Record { [[Key]]: key, [[Value]]: normalizedValuevalue }.
Append p as the last element of entries.
Return M.
2048 |
2049 |
2050 |
2051 |
2052 |
2053 |
2.1.4.3WeakSet Objects
2054 |
WeakSet objects are collections of objects. A distinct object may only occur once as an element of a WeakSet's collection. A WeakSet may be queried to see if it contains a specific object, but no mechanism is provided for enumerating the objects it holds. If an object that is contained by a WeakSet is only reachable by following a chain of references that start within that WeakSet, then that object is inaccessible and is automatically removed from the WeakSet. WeakSet implementations must detect and remove such objects and any associated resources.
2055 |
An implementation may impose an arbitrarily determined latency between the time an object contained in a WeakSet becomes inaccessible and the time when the object is removed from the WeakSet. If this latency was observable to ECMAScript program, it would be a source of indeterminacy that could impact program execution. For that reason, an ECMAScript implementation must not provide any means to determine if a WeakSet contains a particular object that does not require the observer to present the observed object.
2056 |
WeakSet objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structure used in this WeakSet objects specification is only intended to describe the required observable semantics of WeakSet objects. It is not intended to be a viable implementation model.
is the initial value of the WeakSet property of the global object.
2067 |
creates and initializes a new WeakSet object when called as a constructor.
2068 |
is not intended to be called as a function and will throw an exception when called in that manner.
2069 |
is designed to be subclassable. It may be used as the value in an extends clause of a class definition. Subclass constructors that intend to inherit the specified WeakSet behaviour must include a super call to the WeakSet constructor to create and initialize the subclass instance with the internal state necessary to support the WeakSet.prototype built-in methods.
2070 |
2071 |
2072 |
2073 |
2.1.4.3.1.1WeakSet ( [ iterable ] )
2074 |
When the WeakSet function is called with optional argument iterable, the following steps are taken:
2075 |
If NewTarget is undefined, throw a TypeError exception.
Let entries be the List that is S.[[WeakSetData]].
For each e that is an element of entries, do
If e is not empty and SameValue(e, normalizedValuevalue) is true, then
Replace the element of entries whose value is e with an element whose value is empty.
Return true.
Return false.
2104 |
2105 | Note
2106 |
The value empty is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.
2107 |
2108 |
2109 |
2110 |
2111 |
2.1.4.3.2.3WeakSet.prototype.has ( value )
2112 |
The following steps are taken:
2113 |
Let S be the this value.
If Type(S) is not Object, throw a TypeError exception.
If S does not have a [[WeakSetData]] internal slot, throw a TypeError exception.
Let normalizedValue be ? NormalizeCoerceValue(S, value).
Let entries be the List that is S.[[WeakSetData]].
All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.
2126 |
2127 |
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
2128 |
2129 |
2130 |
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2131 |
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
2132 |
Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.
2133 |
2134 |
2135 |
THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17 |
18 | 1. Let _target_ be _collection_.[[CoerceHandler]].
19 | 1. Let _trap_ be ? GetMethod(_target_, `"coerceKey"`).
20 | 1. If _trap_ is undefined
21 | 1. Return _data_.
22 | 1. return Call(_trap_, _target_, « _data_, _collection_ »).
23 |
24 |
25 |
26 |
27 |
NormalizeCoerceValue( _collection_ , _data_ )
28 |
29 | 1. Let _target_ be _collection_.[[CoerceHandler]].
30 | 1. Let _trap_ be ? GetMethod(_target_, `"coerceValue"`).
31 | 1. If _trap_ is undefined
32 | 1. Return _data_.
33 | 1. return Call(_trap_, _target_, « _data_, _collection_ »).
34 |
35 |
36 |
37 |
38 |
Modified algorithms
39 |
40 |
41 |
Keyed Collections
42 |
43 |
44 |
Map Objects
45 |
Map objects are collections of key/value pairs where both the keys and values may be arbitrary ECMAScript language values. A distinct key value may only occur in one key/value pair within the Map's collection. Distinct key values are discriminated using the SameValueZero comparison algorithm.
46 |
Map object must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structures used in this Map objects specification is only intended to describe the required observable semantics of Map objects. It is not intended to be a viable implementation model.
47 |
48 |
49 |
The Map Constructor
50 |
The Map constructor:
51 |
52 |
is the intrinsic object %Map%.
53 |
is the initial value of the `Map` property of the global object.
54 |
creates and initializes a new Map object when called as a constructor.
55 |
is not intended to be called as a function and will throw an exception when called in that manner.
56 |
is designed to be subclassable. It may be used as the value in an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified `Map` behaviour must include a `super` call to the `Map` constructor to create and initialize the subclass instance with the internal state necessary to support the `Map.prototype` built-in methods.
57 |
58 |
59 |
60 |
Map ( [ _iterable_ [, _options_ ] ] )
61 |
When the `Map` function is called with optional argument _iterable_, the following steps are taken:
62 |
63 | 1. If NewTarget is *undefined*, throw a *TypeError* exception.
64 | 1. Let _map_ be ? OrdinaryCreateFromConstructor(NewTarget, `"%MapPrototype%"`, « [[MapData]], [[CoerceHandler]] »).
65 | 1. Set _map_.[[MapData]] to a new empty List.
66 | 1. If Type(_options_) is Object,
67 | 1. Set _map_.[[CoerceHandler]] to _options_.
68 |
69 | 1. Else,
70 | 1. Set _map_.[[CoerceHandler]] to ObjectCreate(*null*).
71 |
72 | 1. If _iterable_ is not present, or is either *undefined* or *null*, return _map_.
73 | 1. Let _adder_ be ? Get(_map_, `"set"`).
74 | 1. Return ? AddEntriesFromIterable(_map_, _iterable_, _adder_).
75 |
76 |
77 |
If the parameter _iterable_ is present, it is expected to be an object that implements an @@iterator method that returns an iterator object that produces a two element array-like object whose first element is a value that will be used as a Map key and whose second element is the value to associate with that key.
78 |
79 |
80 |
81 |
82 |
83 |
Properties of the Map Prototype Object
84 |
The Map prototype object:
85 |
86 |
is the intrinsic object %MapPrototype%.
87 |
has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
88 |
is an ordinary object.
89 |
does not have a [[MapData]] internal slot.
90 |
does not have a [[CoerceHandler]] internal slot.
91 |
92 |
93 |
94 |
Map.prototype.delete ( _key_ )
95 |
The following steps are taken:
96 |
97 | 1. Let _M_ be the *this* value.
98 | 1. If Type(_M_) is not Object, throw a *TypeError* exception.
99 | 1. If _M_ does not have a [[MapData]] internal slot, throw a *TypeError* exception.
100 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_).
101 | 1. Let _entries_ be the List that is _M_.[[MapData]].
102 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do
103 | 1. If _p_.[[Key]] is not ~empty~ and SameValueZero(_p_.[[Key]], _normalizedKey__key_) is *true*, then
104 | 1. Set _p_.[[Key]] to ~empty~.
105 | 1. Set _p_.[[Value]] to ~empty~.
106 | 1. Return *true*.
107 | 1. Return *false*.
108 |
109 |
110 |
The value ~empty~ is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.
111 |
112 |
113 |
114 |
115 |
Map.prototype.get ( _key_ )
116 |
The following steps are taken:
117 |
118 | 1. Let _M_ be the *this* value.
119 | 1. If Type(_M_) is not Object, throw a *TypeError* exception.
120 | 1. If _M_ does not have a [[MapData]] internal slot, throw a *TypeError* exception.
121 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_).
122 | 1. Let _entries_ be the List that is _M_.[[MapData]].
123 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do
124 | 1. If _p_.[[Key]] is not ~empty~ and SameValueZero(_p_.[[Key]], _normalizedKey__key_) is *true*, return _p_.[[Value]].
125 | 1. Return *undefined*.
126 |
127 |
128 |
129 |
130 |
Map.prototype.has ( _key_ )
131 |
The following steps are taken:
132 |
133 | 1. Let _M_ be the *this* value.
134 | 1. If Type(_M_) is not Object, throw a *TypeError* exception.
135 | 1. If _M_ does not have a [[MapData]] internal slot, throw a *TypeError* exception.
136 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_).
137 | 1. Let _entries_ be the List that is _M_.[[MapData]].
138 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do
139 | 1. If _p_.[[Key]] is not ~empty~ and SameValueZero(_p_.[[Key]], _normalizedKey__key_) is *true*, return *true*.
140 | 1. Return *false*.
141 |
142 |
143 |
144 |
145 |
Map.prototype.set ( _key_, _value_ )
146 |
The following steps are taken:
147 |
148 | 1. Let _M_ be the *this* value.
149 | 1. If Type(_M_) is not Object, throw a *TypeError* exception.
150 | 1. If _M_ does not have a [[MapData]] internal slot, throw a *TypeError* exception.
151 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_).
152 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_M_, _value_).
153 | 1. Let _entries_ be the List that is _M_.[[MapData]].
154 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do
155 | 1. If _p_.[[Key]] is not ~empty~ and SameValueZero(_p_.[[Key]], _normalizedKey__key_) is *true*, then
156 | 1. Set _p_.[[Value]] to _normalizedValue__value_.
157 | 1. Return _M_.
158 | 1. If _key_ is *-0*, set _key_ to *+0*.
159 | 1. Let _p_ be the Record { [[Key]]: _key_, [[Value]]: _normalizedValue__value_ }.
160 | 1. Append _p_ as the last element of _entries_.
161 | 1. Return _M_.
162 |
163 |
164 |
165 |
166 |
Properties of Map Instances
167 |
Map instances are ordinary objects that inherit properties from the Map prototype. Map instances also have [[MapData]], and [[CoerceHandler]] internal slots.
168 |
169 |
170 |
171 |
172 |
Set Objects
173 |
Set objects are collections of ECMAScript language values. A distinct value may only occur once as an element of a Set's collection. Distinct values are discriminated using the SameValueZero comparison algorithm.
174 |
Set objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structures used in this Set objects specification is only intended to describe the required observable semantics of Set objects. It is not intended to be a viable implementation model.
175 |
176 |
177 |
The Set Constructor
178 |
The Set constructor:
179 |
180 |
is the intrinsic object %Set%.
181 |
is the initial value of the `Set` property of the global object.
182 |
creates and initializes a new Set object when called as a constructor.
183 |
is not intended to be called as a function and will throw an exception when called in that manner.
184 |
is designed to be subclassable. It may be used as the value in an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified `Set` behaviour must include a `super` call to the `Set` constructor to create and initialize the subclass instance with the internal state necessary to support the `Set.prototype` built-in methods.
185 |
186 |
187 |
188 |
Set ( [ _iterable_ [, _options_ ]] )
189 |
When the `Set` function is called with optional argument _iterable_, the following steps are taken:
190 |
191 | 1. If NewTarget is *undefined*, throw a *TypeError* exception.
192 | 1. Let _set_ be ? OrdinaryCreateFromConstructor(NewTarget, `"%SetPrototype%"`, « [[SetData]] »).
193 | 1. Set _set_.[[SetData]] to a new empty List.
194 |
195 | 1. If Type(_options_) is Object,
196 | 1. Set _map_.[[CoerceHandler]] to _options_.
197 |
198 | 1. Else,
199 | 1. Set _map_.[[CoerceHandler]] to ObjectCreate(*null*).
200 |
201 | 1. If _iterable_ is not present, set _iterable_ to *undefined*.
202 | 1. If _iterable_ is either *undefined* or *null*, return _set_.
203 | 1. Let _adder_ be ? Get(_set_, `"add"`).
204 | 1. If IsCallable(_adder_) is *false*, throw a *TypeError* exception.
205 | 1. Let _iteratorRecord_ be ? GetIterator(_iterable_).
206 | 1. Repeat,
207 | 1. Let _next_ be ? IteratorStep(_iteratorRecord_).
208 | 1. If _next_ is *false*, return _set_.
209 | 1. Let _nextValue_ be ? IteratorValue(_next_).
210 | 1. Let _status_ be Call(_adder_, _set_, « _nextValue_ »).
211 | 1. If _status_ is an abrupt completion, return ? IteratorClose(_iteratorRecord_, _status_).
212 |
213 |
214 |
215 |
216 |
217 |
Properties of the Set Prototype Object
218 |
The Set prototype object:
219 |
220 |
is the intrinsic object %SetPrototype%.
221 |
has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
222 |
is an ordinary object.
223 |
does not have a [[SetData]] internal slot.
224 |
does not have a [[CoerceHandler]] internal slot.
225 |
226 |
227 |
228 |
Set.prototype.add ( _value_ )
229 |
The following steps are taken:
230 |
231 | 1. Let _S_ be the *this* value.
232 | 1. If Type(_S_) is not Object, throw a *TypeError* exception.
233 | 1. If _S_ does not have a [[SetData]] internal slot, throw a *TypeError* exception.
234 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_).
235 | 1. Let _entries_ be the List that is _S_.[[SetData]].
236 | 1. For each _e_ that is an element of _entries_, do
237 | 1. If _e_ is not ~empty~ and SameValueZero(_e_, _normalizedValue__value_) is *true*, then
238 | 1. Return _S_.
239 | 1. If _value_ is *-0*, set _value_ to *+0*.
240 | 1. Append _value_ as the last element of _entries_.
241 | 1. Return _S_.
242 |
243 |
244 |
245 |
246 |
Set.prototype.delete ( _value_ )
247 |
The following steps are taken:
248 |
249 | 1. Let _S_ be the *this* value.
250 | 1. If Type(_S_) is not Object, throw a *TypeError* exception.
251 | 1. If _S_ does not have a [[SetData]] internal slot, throw a *TypeError* exception.
252 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_).
253 | 1. Let _entries_ be the List that is _S_.[[SetData]].
254 | 1. For each _e_ that is an element of _entries_, do
255 | 1. If _e_ is not ~empty~ and SameValueZero(_e_, _normalizedValue__value_) is *true*, then
256 | 1. Replace the element of _entries_ whose value is _e_ with an element whose value is ~empty~.
257 | 1. Return *true*.
258 | 1. Return *false*.
259 |
260 |
261 |
The value ~empty~ is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.
262 |
263 |
264 |
265 |
266 |
Set.prototype.has ( _value_ )
267 |
The following steps are taken:
268 |
269 | 1. Let _S_ be the *this* value.
270 | 1. If Type(_S_) is not Object, throw a *TypeError* exception.
271 | 1. If _S_ does not have a [[SetData]] internal slot, throw a *TypeError* exception.
272 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_).
273 | 1. Let _entries_ be the List that is _S_.[[SetData]].
274 | 1. For each _e_ that is an element of _entries_, do
275 | 1. If _e_ is not ~empty~ and SameValueZero(_e_, _normalizedValue__value_) is *true*, return *true*.
276 | 1. Return *false*.
277 |
278 |
279 |
280 |
281 |
282 |
283 |
WeakMap Objects
284 |
WeakMap objects are collections of key/value pairs where the keys are objects and values may be arbitrary ECMAScript language values. A WeakMap may be queried to see if it contains a key/value pair with a specific key, but no mechanism is provided for enumerating the objects it holds as keys. If an object that is being used as the key of a WeakMap key/value pair is only reachable by following a chain of references that start within that WeakMap, then that key/value pair is inaccessible and is automatically removed from the WeakMap. WeakMap implementations must detect and remove such key/value pairs and any associated resources.
285 |
An implementation may impose an arbitrarily determined latency between the time a key/value pair of a WeakMap becomes inaccessible and the time when the key/value pair is removed from the WeakMap. If this latency was observable to ECMAScript program, it would be a source of indeterminacy that could impact program execution. For that reason, an ECMAScript implementation must not provide any means to observe a key of a WeakMap that does not require the observer to present the observed key.
286 |
WeakMap objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of key/value pairs in the collection. The data structure used in this WeakMap objects specification are only intended to describe the required observable semantics of WeakMap objects. It is not intended to be a viable implementation model.
287 |
288 |
WeakMap and WeakSets are intended to provide mechanisms for dynamically associating state with an object in a manner that does not “leak” memory resources if, in the absence of the WeakMap or WeakSet, the object otherwise became inaccessible and subject to resource reclamation by the implementation's garbage collection mechanisms. This characteristic can be achieved by using an inverted per-object mapping of weak map instances to keys. Alternatively each weak map may internally store its key to value mappings but this approach requires coordination between the WeakMap or WeakSet implementation and the garbage collector. The following references describe mechanism that may be useful to implementations of WeakMap and WeakSets:
289 |
Barry Hayes. 1997. Ephemerons: a new finalization mechanism. In Proceedings of the 12th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications (OOPSLA '97), A. Michael Berman (Ed.). ACM, New York, NY, USA, 176-183, http://doi.acm.org/10.1145/263698.263733.
is the initial value of the `WeakMap` property of the global object.
299 |
creates and initializes a new WeakMap object when called as a constructor.
300 |
is not intended to be called as a function and will throw an exception when called in that manner.
301 |
is designed to be subclassable. It may be used as the value in an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified `WeakMap` behaviour must include a `super` call to the `WeakMap` constructor to create and initialize the subclass instance with the internal state necessary to support the `WeakMap.prototype` built-in methods.
302 |
303 |
304 |
305 |
WeakMap ( [ _iterable_ [, _options_ ]] )
306 |
When the `WeakMap` function is called with optional argument _iterable_, the following steps are taken:
307 |
308 | 1. If NewTarget is *undefined*, throw a *TypeError* exception.
309 | 1. Let _map_ be ? OrdinaryCreateFromConstructor(NewTarget, `"%WeakMapPrototype%"`, « [[WeakMapData]] »).
310 | 1. Set _map_.[[WeakMapData]] to a new empty List.
311 | 1. If Type(_options_) is Object,
312 | 1. Set _map_.[[CoerceHandler]] to _options_.
313 |
314 | 1. Else,
315 | 1. Set _map_.[[CoerceHandler]] to ObjectCreate(*null*).
316 |
317 | 1. If _iterable_ is not present, or is either *undefined* or *null*, return _map_.
318 | 1. Let _adder_ be ? Get(_map_, `"set"`).
319 | 1. Return ? AddEntriesFromIterable(_map_, _iterable_, _adder_).
320 |
321 |
322 |
If the parameter _iterable_ is present, it is expected to be an object that implements an @@iterator method that returns an iterator object that produces a two element array-like object whose first element is a value that will be used as a WeakMap key and whose second element is the value to associate with that key.
323 |
324 |
325 |
326 |
327 |
328 |
Properties of the WeakMap Prototype Object
329 |
The WeakMap prototype object:
330 |
331 |
is the intrinsic object %WeakMapPrototype%.
332 |
has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
333 |
is an ordinary object.
334 |
does not have a [[WeakMapData]] internal slot.
335 |
does not have a [[CoerceHandler]] internal slot.
336 |
337 |
338 |
339 |
WeakMap.prototype.delete ( _key_ )
340 |
The following steps are taken:
341 |
342 | 1. Let _M_ be the *this* value.
343 | 1. If Type(_M_) is not Object, throw a *TypeError* exception.
344 | 1. If _M_ does not have a [[WeakMapData]] internal slot, throw a *TypeError* exception.
345 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_).
346 | 1. Let _entries_ be the List that is _M_.[[WeakMapData]].
347 | 1. If Type(_key_) is not Object, return *false*.
348 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do
349 | 1. If _p_.[[Key]] is not ~empty~ and SameValue(_p_.[[Key]], _normalizedKey__key_) is *true*, then
350 | 1. Set _p_.[[Key]] to ~empty~.
351 | 1. Set _p_.[[Value]] to ~empty~.
352 | 1. Return *true*.
353 | 1. Return *false*.
354 |
355 |
356 |
The value ~empty~ is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.
357 |
358 |
359 |
360 |
361 |
WeakMap.prototype.get ( _key_ )
362 |
The following steps are taken:
363 |
364 | 1. Let _M_ be the *this* value.
365 | 1. If Type(_M_) is not Object, throw a *TypeError* exception.
366 | 1. If _M_ does not have a [[WeakMapData]] internal slot, throw a *TypeError* exception.
367 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_).
368 | 1. Let _entries_ be the List that is _M_.[[WeakMapData]].
369 | 1. If Type(_key_) is not Object, return *undefined*.
370 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do
371 | 1. If _p_.[[Key]] is not ~empty~ and SameValue(_p_.[[Key]], _normalizedKey__key_) is *true*, return _p_.[[Value]].
372 | 1. Return *undefined*.
373 |
374 |
375 |
376 |
377 |
WeakMap.prototype.has ( _key_ )
378 |
The following steps are taken:
379 |
380 | 1. Let _M_ be the *this* value.
381 | 1. If Type(_M_) is not Object, throw a *TypeError* exception.
382 | 1. If _M_ does not have a [[WeakMapData]] internal slot, throw a *TypeError* exception.
383 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_).
384 | 1. Let _entries_ be the List that is _M_.[[WeakMapData]].
385 | 1. If Type(_key_) is not Object, return *false*.
386 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do
387 | 1. If _p_.[[Key]] is not ~empty~ and SameValue(_p_.[[Key]], _normalizedKey__key_) is *true*, return *true*.
388 | 1. Return *false*.
389 |
390 |
391 |
392 |
393 |
WeakMap.prototype.set ( _key_, _value_ )
394 |
The following steps are taken:
395 |
396 | 1. Let _M_ be the *this* value.
397 | 1. If Type(_M_) is not Object, throw a *TypeError* exception.
398 | 1. If _M_ does not have a [[WeakMapData]] internal slot, throw a *TypeError* exception.
399 | 1. Let _normalizedKey_ be ? NormalizeCoerceKey(_M_, _key_).
400 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_M_, _value_).
401 | 1. Let _entries_ be the List that is _M_.[[WeakMapData]].
402 | 1. If Type(_key_) is not Object, throw a *TypeError* exception.
403 | 1. For each Record { [[Key]], [[Value]] } _p_ that is an element of _entries_, do
404 | 1. If _p_.[[Key]] is not ~empty~ and SameValue(_p_.[[Key]], _normalizedKey__key_) is *true*, then
405 | 1. Set _p_.[[Value]] to _value_.
406 | 1. Return _M_.
407 | 1. Let _p_ be the Record { [[Key]]: _key_, [[Value]]: _normalizedValue__value_ }.
408 | 1. Append _p_ as the last element of _entries_.
409 | 1. Return _M_.
410 |
411 |
412 |
413 |
414 |
415 |
WeakSet Objects
416 |
WeakSet objects are collections of objects. A distinct object may only occur once as an element of a WeakSet's collection. A WeakSet may be queried to see if it contains a specific object, but no mechanism is provided for enumerating the objects it holds. If an object that is contained by a WeakSet is only reachable by following a chain of references that start within that WeakSet, then that object is inaccessible and is automatically removed from the WeakSet. WeakSet implementations must detect and remove such objects and any associated resources.
417 |
An implementation may impose an arbitrarily determined latency between the time an object contained in a WeakSet becomes inaccessible and the time when the object is removed from the WeakSet. If this latency was observable to ECMAScript program, it would be a source of indeterminacy that could impact program execution. For that reason, an ECMAScript implementation must not provide any means to determine if a WeakSet contains a particular object that does not require the observer to present the observed object.
418 |
WeakSet objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structure used in this WeakSet objects specification is only intended to describe the required observable semantics of WeakSet objects. It is not intended to be a viable implementation model.
419 |
420 |
See the NOTE in .
421 |
422 |
423 |
424 |
The WeakSet Constructor
425 |
The WeakSet constructor:
426 |
427 |
is the intrinsic object %WeakSet%.
428 |
is the initial value of the `WeakSet` property of the global object.
429 |
creates and initializes a new WeakSet object when called as a constructor.
430 |
is not intended to be called as a function and will throw an exception when called in that manner.
431 |
is designed to be subclassable. It may be used as the value in an `extends` clause of a class definition. Subclass constructors that intend to inherit the specified `WeakSet` behaviour must include a `super` call to the `WeakSet` constructor to create and initialize the subclass instance with the internal state necessary to support the `WeakSet.prototype` built-in methods.
432 |
433 |
434 |
435 |
WeakSet ( [ _iterable_ ] )
436 |
When the `WeakSet` function is called with optional argument _iterable_, the following steps are taken:
437 |
438 | 1. If NewTarget is *undefined*, throw a *TypeError* exception.
439 | 1. Let _set_ be ? OrdinaryCreateFromConstructor(NewTarget, `"%WeakSetPrototype%"`, « [[WeakSetData]] »).
440 | 1. Set _set_.[[WeakSetData]] to a new empty List.
441 | 1. If Type(_options_) is Object,
442 | 1. Set _map_.[[CoerceHandler]] to _options_.
443 |
444 | 1. Else,
445 | 1. Set _map_.[[CoerceHandler]] to ObjectCreate(*null*).
446 |
447 | 1. If _iterable_ is not present, set _iterable_ to *undefined*.
448 | 1. If _iterable_ is either *undefined* or *null*, return _set_.
449 | 1. Let _adder_ be ? Get(_set_, `"add"`).
450 | 1. If IsCallable(_adder_) is *false*, throw a *TypeError* exception.
451 | 1. Let _iteratorRecord_ be ? GetIterator(_iterable_).
452 | 1. Repeat,
453 | 1. Let _next_ be ? IteratorStep(_iteratorRecord_).
454 | 1. If _next_ is *false*, return _set_.
455 | 1. Let _nextValue_ be ? IteratorValue(_next_).
456 | 1. Let _status_ be Call(_adder_, _set_, « _nextValue_ »).
457 | 1. If _status_ is an abrupt completion, return ? IteratorClose(_iteratorRecord_, _status_).
458 |
459 |
460 |
461 |
462 |
463 |
Properties of the WeakSet Prototype Object
464 |
The WeakSet prototype object:
465 |
466 |
is the intrinsic object %WeakSetPrototype%.
467 |
has a [[Prototype]] internal slot whose value is the intrinsic object %ObjectPrototype%.
468 |
is an ordinary object.
469 |
does not have a [[WeakSetData]] internal slot.
470 |
does not have a [[CoerceHandler]] internal slot.
471 |
472 |
473 |
474 |
WeakSet.prototype.add ( _value_ )
475 |
The following steps are taken:
476 |
477 | 1. Let _S_ be the *this* value.
478 | 1. If Type(_S_) is not Object, throw a *TypeError* exception.
479 | 1. If _S_ does not have a [[WeakSetData]] internal slot, throw a *TypeError* exception.
480 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_).
481 | 1. If Type(_value_) is not Object, throw a *TypeError* exception.
482 | 1. Let _entries_ be the List that is _S_.[[WeakSetData]].
483 | 1. For each _e_ that is an element of _entries_, do
484 | 1. If _e_ is not ~empty~ and SameValue(_e_, _normalizedValue__value_) is *true*, then
485 | 1. Return _S_.
486 | 1. Append _value_ as the last element of _entries_.
487 | 1. Return _S_.
488 |
489 |
490 |
491 |
492 |
WeakSet.prototype.delete ( _value_ )
493 |
The following steps are taken:
494 |
495 | 1. Let _S_ be the *this* value.
496 | 1. If Type(_S_) is not Object, throw a *TypeError* exception.
497 | 1. If _S_ does not have a [[WeakSetData]] internal slot, throw a *TypeError* exception.
498 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_).
499 | 1. If Type(_value_) is not Object, return *false*.
500 | 1. Let _entries_ be the List that is _S_.[[WeakSetData]].
501 | 1. For each _e_ that is an element of _entries_, do
502 | 1. If _e_ is not ~empty~ and SameValue(_e_, _normalizedValue__value_) is *true*, then
503 | 1. Replace the element of _entries_ whose value is _e_ with an element whose value is ~empty~.
504 | 1. Return *true*.
505 | 1. Return *false*.
506 |
507 |
508 |
The value ~empty~ is used as a specification device to indicate that an entry has been deleted. Actual implementations may take other actions such as physically removing the entry from internal data structures.
509 |
510 |
511 |
512 |
513 |
WeakSet.prototype.has ( _value_ )
514 |
The following steps are taken:
515 |
516 | 1. Let _S_ be the *this* value.
517 | 1. If Type(_S_) is not Object, throw a *TypeError* exception.
518 | 1. If _S_ does not have a [[WeakSetData]] internal slot, throw a *TypeError* exception.
519 | 1. Let _normalizedValue_ be ? NormalizeCoerceValue(_S_, _value_).
520 | 1. Let _entries_ be the List that is _S_.[[WeakSetData]].
521 | 1. If Type(_value_) is not Object, return *false*.
522 | 1. For each _e_ that is an element of _entries_, do
523 | 1. If _e_ is not ~empty~ and SameValue(_e_, _normalizedValue__value_) is *true*, return *true*.
524 | 1. Return *false*.
525 |
526 |
527 |
528 |
529 |
--------------------------------------------------------------------------------