├── README.md
├── TC39-March2015.key
├── TC39-March2015.pdf
├── es6.js
├── examples.js
└── polyfill-spec.js
/README.md:
--------------------------------------------------------------------------------
1 | # The *ReverseIterable* Interface
2 |
3 | **Stage:** 0, Strawman
4 |
5 | **Author:** Lee Byron
6 |
7 | Iterating through collections in reverse is a pretty common operation in
8 | application logic. In imperative code, we see these two patterns all the time:
9 |
10 | ```js
11 | // Forward iteration
12 | for (var i = 0; i < a.length; i++) {
13 | var v = a[i];
14 | doSomething(v, i);
15 | }
16 |
17 | // Reverse iteration
18 | for (var i = a.length - 1; i >= 0; i--) {
19 | var v = a[i];
20 | doSomething(v, i);
21 | }
22 | ```
23 |
24 | Iterators in ES2015 are a valuable higher order abstraction which helps capture
25 | forward iteration. The `for of` loop can simplify the imperative forward
26 | iteration to become:
27 |
28 | ```js
29 | for (let [i, v] of a.entries()) {
30 | doSomething(v, i);
31 | }
32 | ```
33 |
34 | We're missing an equivalent abstraction to capture the reverse iteration. In
35 | this proposal, it's suggested that this can be written as:
36 |
37 | ```js
38 | for (let [i, v] of a.entries().reverse()) {
39 | doSomething(v, i);
40 | }
41 | ```
42 |
43 | A default reverse iterator can also be retrieved similarly to a default iterator:
44 |
45 | ```js
46 | for (let v of a[Symbol.reverseIterator]()) {
47 | doSomething(v);
48 | }
49 | ```
50 |
51 |
52 | This syntax has the benefit of not introducing new syntactical concepts but
53 | instead just adds a few function properties to iterator prototypes.
54 |
55 | In addition to the `reverse()` method, this proposal also suggests the
56 | addition of a *ReverseIterable* interface which any object can implement by
57 | adding a function to the `Symbol.reverseIterator` property. Capturing this in an
58 | interface allows arbitrary code to detect that a particular object is reverse
59 | iterable and use that to it's advantage.
60 |
61 |
62 | ## FAQ
63 |
64 | ##### What happens if `reverse()` is called on something not easily reversed, like a Generator function?
65 |
66 | This proposal suggests one-way iterables remain one-way. Objects which implement
67 | *Iterable* do not also have to implement *ReverseIterable*. There is no buffering
68 | in the native implementation of `%IteratorPrototype%.reverse()` when called on an
69 | object which is not *ReverseIterable*, instead a TypeError exception is thrown.
70 |
71 | Buffering can result in difficult to understand performance and memory pressure
72 | in some cases and infinite buffers in the worst case.
73 |
74 | Specifically, Generator objects *do not* implement the *ReverseIterable* interface.
75 |
76 | For example, this code will throw a TypeError exception with a useful message:
77 |
78 | ```js
79 | function* fib () {
80 | var n1 = 0, n2 = 1;
81 | yield n1;
82 | [n1, n2] = [n2, n1 + n2];
83 | }
84 |
85 | try {
86 | let fibs = fib();
87 | for (let num of fibs.reverse()) {
88 | console.log(num);
89 | }
90 | } catch (e) {
91 | assert(e.message === "Iterator is not reversable.");
92 | }
93 | ```
94 |
95 | ##### What happens if `reverse()` is called after an iterator has already been partially iterated?
96 |
97 | This proposal suggests that such a sequence of operations is not allowed. When
98 | `[Symbol.reverseIterator]()` is called on an ArrayIterator or ListIterator, it
99 | will first check that iteration has not already begun otherwise throw a
100 | TypeError with a useful message:
101 |
102 | ```js
103 | var iterator = array.values();
104 | iterator.next();
105 |
106 | try {
107 | var reverseIterator = iterator.reverse();
108 | } catch (e) {
109 | assert(e.message === "Cannot reverse once iteration has begun.");
110 | }
111 | ```
112 |
113 | An alternative to this could be starting the partially completed reversed
114 | iterator at the same position:
115 |
116 | ```js
117 | var array = ["A", "B", "C"];
118 | var iterator = array.values();
119 | console.log(iterator.next().value); // "A"
120 | console.log(iterator.next().value); // "B"
121 | var reverseIterator = iterator.reverse();
122 | console.log(reverseIterator.next().value); // "A"
123 | console.log(reverseIterator.next().value); // undefined
124 | ```
125 |
126 | There are some real caveats to this alternative approach:
127 |
128 | * The behavioral differences between reversing fresh iterators and partially
129 | iterated iterators could be confusing.
130 | * This requires maintaing a small amount of additional state and providing that
131 | state to the `[Symbol.reverseIterator]` method in a standardized way.
132 | * Completed iterators would require maintaining their state in case they are
133 | reversed which could make memory leaks easier to introduce to a program.
134 |
135 | Because of these caveats, this alternative is not being proposed.
136 |
137 |
138 | # Additions to Spec
139 |
140 | ## 6.1.5.1 Well-Known Symbols
141 | > One row is added to Table 1.
142 |
143 | | Specification Name | [[Description]] | Value and Purpose |
144 | | ------------------ | --------------- | ----------------- |
145 | | @@reverseIterator | "Symbol.reverseIterator" | A method that returns the default reverse iterator for an object. Called by the **reverse** method of Iterators. |
146 |
147 |
148 |
149 | ## 25.1.1 Common Iteration Interfaces
150 |
151 | ### 25.1.1.X The *ReverseIterable* Interface
152 | > This interface is new
153 |
154 | The *ReverseIterable* interface includes the following property:
155 |
156 | | Property | Value | Requirements |
157 | | -------- | ----- | ------------ |
158 | | @@reverseIterator | A function that returns an Iterator object. | The returned object must conform to the *Iterator* interface. It must iterate through values in the reverse order of the object returned from the `@@iterator` method. |
159 |
160 | NOTE An object should implement the *ReverseIterable* interface only when it
161 | also implements the *Iterable* interface.
162 |
163 |
164 |
165 | ## 25.1.2 The %IteratorPrototype% Object
166 |
167 | #### 25.1.2.1.X %IteratorPrototype%.reverse ( )
168 | > This property is new
169 |
170 | The following steps are taken:
171 |
172 | 1. Let *O* be the result of calling ToObject with the **this** value as its argument.
173 | 2. ReturnIfAbrupt(*O*).
174 | 3. Let *usingReverseIterator* be GetMethod(*O*, @@reverseIterator).
175 | 4. If *usingReverseIterator* is **undefined**, throw a **TypeError** exception.
176 | 5. Let *iterator* be GetIterator(*O*, *usingReverseIterator*).
177 | 6. return *iterator*.
178 |
179 |
180 |
181 | ## 19.4.2 Properties of the Symbol Constructor
182 |
183 | ### 19.4.2.X Symbol.reverseIterator
184 | > This property is new
185 |
186 | The initial value of Symbol.reverseIterator is the well known symbol @@reverseIterator (Table 1).
187 |
188 | This property has the attributes { [[Writable]]: **false**, [[Enumerable]]: **false**, [[Configurable]]: **false** }
189 |
190 |
191 |
192 | ## 22.1.3 Properties of the Array Prototype Object
193 |
194 | ### 22.1.3.X Array.prototype \[ @@reverseIterator ] ( )
195 | > This property is new
196 |
197 | 1. Let *O* be the result of calling ToObject with the **this** value as its argument.
198 | 2. ReturnIfAbrupt(*O*).
199 | 3. Return CreateArrayReverseIterator(*O*, **"value"**).
200 |
201 |
202 |
203 | ## 22.1.5 Array Iterator Objects
204 |
205 | ### 22.1.5.2 The %ArrayIteratorPrototype% Object
206 |
207 | #### 22.1.5.2.X ArrayIteratorPrototype \[ @@reverseIterator ] ( )
208 | > This property is new
209 |
210 | 1. Let *O* be the **this** value.
211 | 2. If Type(*O*) is not Object, throw a **TypeError** exception.
212 | 3. If *O* does not have all of the internal slots of an Array Iterator Instance, throw a **TypeError** exception.
213 | 4. Let *a* be the value of the [[IteratedObject]] internal slot of *O*.
214 | 5. Let *index* be the value of the [[ArrayIteratorNextIndex]] internal slot of *O*.
215 | 6. If *index* !== 0, then throw a **TypeError** exception.
216 | 7. Let *itemKind* be the value of the [[ArrayIterationKind]] internal slot of *O*.
217 | 8. Return CreateArrayReverseIterator(*a*, *itemKind*).
218 |
219 |
220 |
221 | ## 22.1.X Array Reverse Iterator Objects
222 | > This section is new
223 |
224 | An Array Reverse Iterator is an object, that represents a specific reverse
225 | iteration over some specific Array instance object. There is not a named
226 | constructor for Array Reverse Iterator objects. Instead, Array Reverse
227 | Iterator objects are created by calling **reverse** on Array Iterator objects.
228 |
229 |
230 | ### 22.1.X.1 CreateArrayReverseIterator Abstract Operation
231 |
232 | 1. Assert: Type(*array*) is Object
233 | 2. Let *iterator* be ObjectCreate(%ArrayIteratorPrototype%, ([[IteratedObject]], [[ArrayReverseIteratorNextIndex]], [[ArrayIterationKind]])).
234 | 3. Set *iterator’s* [[IteratedObject]] internal slot to *array*.
235 | 4. If *array* has a [[TypedArrayName]] internal slot, then
236 | * a. Let *len* be the value of the [[ArrayLength]] internal slot of *array*.
237 | 5. Else,
238 | * a. Let *len* be ToLength(Get(*array*, **"length"**)).
239 | * b. ReturnIfAbrupt(*len*).
240 | 6. Set *iterator’s* [[ArrayReverseIteratorNextIndex]] internal slot to *len*-1.
241 | 7. Set *iterator’s* [[ArrayIteratorKind]] internal slot to *kind*.
242 | 8. Return *iterator*.
243 |
244 |
245 | ### 22.1.X.2 The %ArrayReverseIteratorPrototype% Object
246 |
247 | All Array Reverse Iterator Objects inherit properties from the
248 | %ArrayReverseIteratorPrototype% intrinsic object. The
249 | %ArrayReverseIteratorPrototype% object is an ordinary object and its
250 | [[Prototype]] internal slot is the %IteratorPrototype% intrinsic object
251 | (25.1.2). In addition, %ArrayReverseIteratorPrototype% has the following
252 | properties:
253 |
254 |
255 | #### 22.1.X.2.1 %ArrayReverseIteratorPrototype%.next ( )
256 |
257 | 1. Let *O* be the **this** value.
258 | 2. If Type(*O*) is not Object, throw a **TypeError** exception.
259 | 3. If *O* does not have all of the internal slots of an Array Reverse Iterator Instance, throw a **TypeError** exception.
260 | 4. Let *a* be the value of the [[IteratedObject]] internal slot of *O*.
261 | 5. If *a* is **undefined**, then return CreateIterResult*O*bject(**undefined**, **true**).
262 | 6. Let *index* be the value of the [[ArrayReverseIteratorNextIndex]] internal slot of *O*.
263 | 7. Let *itemKind* be the value of the [[ArrayIterationKind]] internal slot of *O*.
264 | 8. If *index* < 0, then
265 | 9. Set the value of the [[ArrayReverseIteratorNextIndex]] internal slot of *O* to *index*-1.
266 | 10. If *itemKind* is **"key"**, return CreateIterResultObject(*index*, **false**).
267 | 11. Let *elementKey* be ToString(*index*).
268 | 12. Let *elementValue* be Get(*a*, *elementKey*).
269 | 13. ReturnIfAbrupt(*elementValue*).
270 | 14. If *itemKind* is **"value"**, let *result* be *elementValue*.
271 | 15. Else,
272 | * a. Assert *itemKind* is **"key+value"**,.
273 | * b. Let *result* be CreateArrayFromList(*«index, elementValue»*).
274 | 16. Return CreateIterResultObject(*result*, **false**).
275 |
276 |
277 | #### 22.1.X.2.2 ArrayReverseIteratorPrototype \[ @@reverseIterator ] ( )
278 |
279 | 1. Let *O* be the **this** value.
280 | 2. If Type(*O*) is not Object, throw a **TypeError** exception.
281 | 3. If *O* does not have all of the internal slots of an Array Reverse Iterator Instance, throw a **TypeError** exception.
282 | 4. Let *a* be the value of the [[IteratedObject]] internal slot of *O*.
283 | 5. Let *index* be the value of the [[ArrayReverseIteratorNextIndex]] internal slot of *O*.
284 | 6. If *a* has a [[TypedArrayName]] internal slot, then
285 | * a. Let *len* be the value of the [[ArrayLength]] internal slot of *a*.
286 | 7. Else,
287 | * a. Let *len* be ToLength(Get(*a*, **"length"**)).
288 | * b. ReturnIfAbrupt(*len*).
289 | 8. If *index* !== *len*-1, then throw a **TypeError** exception.
290 | 9. Let *itemKind* be the value of the [[ArrayIterationKind]] internal slot of *O*.
291 | 10. Return CreateArrayIterator(*a*, *itemKind*).
292 |
293 |
294 | #### 22.1.X.2.3 %ArrayIteratorPrototype% \[ @@toStringTag ]
295 |
296 | The initial value of the @@toStringTag property is the string value
297 | **"Array Reverse Iterator"**.
298 |
299 | This property has the attributes { [[Writable]]: **false**, [[Enumerable]]: **false**, [[Configurable]]: **true** }.
300 |
301 |
302 | ### 22.1.X.3 Properties of Array Reverse Iterator
303 |
304 | Array Reverse Iterator instances are ordinary objects that inherit properties
305 | from the %ArrayReverseIteratorPrototype% intrinsic object. Array Reverse
306 | Iterator instances are initially created with the internal slots listed in the
307 | following table.
308 |
309 | | Internal Slot | Description |
310 | | --------------------------------- | ----------- |
311 | | [[IteratedObject]] | The object whose array elements are being iterated. |
312 | | [[ArrayReverseIteratorNextIndex]] | The integer index of the next array index to be examined by this iteration. |
313 | | [[ArrayIterationKind]] | A string value that identifies what is to be returned for each element of the iteration. The possible values are: **"key"**, **"value"**, **"key+value"**. |
314 |
315 |
316 |
317 | ## 21.1.X String Reverse Iterator Objects
318 | > This section is new
319 |
320 | *TK* changes will mirror Array changes.
321 |
322 |
323 |
324 | ## 23.1.X Map Reverse Iterator Objects
325 | > This section is new
326 |
327 | *TK* changes will mirror Array changes.
328 |
329 |
330 |
331 | ## 23.2.X Set Reverse Iterator Objects
332 | > This section is new
333 |
334 | *TK* changes will mirror Array changes.
335 |
336 |
337 |
338 | ## 7.4 Operations on Iterator Objects
339 |
340 | ### 7.4.8 CreateListIterator ( list )
341 | > This existing abstract operation has 2 new steps added: 7 and 8.
342 |
343 | 1. Let *iterator* be ObjectCreate(%IteratorPrototype%, ([[IteratorNext]], [[IteratedList]], [[ListIteratorNextIndex]])).
344 | 2. Set *iterator’s* [[IteratedList]] internal slot to *list*.
345 | 3. Set *iterator’s* [[ListIteratorNextIndex]] internal slot to 0.
346 | 4. Let *next* be a new built-in function object as defined in ListIterator **next** (7.4.8.1).
347 | 5. Set *iterator’s* [[IteratorNext]] internal slot to *next*.
348 | 6. Perform CreateMethodProperty(*iterator*, **"next"**, *next*).
349 | 7. Let *reverse* be the built-in function object ListIterator **reverse**.
350 | 8. Perform CreateMethodProperty(*iterator*, @@reverseIterator, *reverse*).
351 | 9. Return *iterator*.
352 |
353 | #### 7.4.8.X ListIterator reverse ( )
354 | > This method is new
355 |
356 | The ListIterator **reverse** method is a standard built-in function object (clause 17)
357 | that performs the following steps:
358 |
359 | 1. Let *O* be the **this** value.
360 | 2. If *O* does not have a [[IteratorList]] internal slot, then throw a **TypeError** exception.
361 | 3. Let *list* be the value of the [[IteratorList]] internal slot of *O*.
362 | 4. Return CreateListReverseIterator(*list*).
363 |
364 |
365 | ### 7.4.X CreateListReverseIterator ( list )
366 | > This abstract operation and section is new
367 |
368 | The abstract operation CreateListReverseIterator with argument list creates an
369 | Iterator (25.1.1.2) object whose next method returns the successive elements of
370 | list in descending (reversed) order. It performs the following steps:
371 |
372 | 1. Let *iterator* be ObjectCreate(%IteratorPrototype%, ([[IteratorNext]], [[IteratedList]], [[ListReverseIteratorNextIndex]])).
373 | 2. Set *iterator’s* [[IteratedList]] internal slot to *list*.
374 | 3. Let *len* be the number of elements of *list*.
375 | 4. Set *iterator’s* [[ListReverseIteratorNextIndex]] internal slot to *len*-1.
376 | 5. Let *next* be a new built-in function object as defined in ListReverseIterator **next**.
377 | 6. Set *iterator’s* [[IteratorNext]] internal slot to *next*.
378 | 7. Perform CreateMethodProperty(*iterator*, **"next"**, *next*).
379 | 8. Let *reverse* be the built-in function object ListReverseIterator **reverse**.
380 | 9. Perform CreateMethodProperty(*iterator*, @@reverseIterator, *reverse*).
381 | 10. Return *iterator*.
382 |
383 | #### 7.4.X.1 ListReverseIterator next ( )
384 |
385 | The ListReverseIterator **next** method is a standard built-in function object (clause 17) that performs the following steps:
386 |
387 | 1. Let *O* be the **this** value.
388 | 2. Let *f* be the active function object.
389 | 3. If *O* does not have a [[IteratorNext]] internal slot, then throw a **TypeError** exception.
390 | 4. Let *next* be the value of the [[IteratorNext]] internal slot of *O*.
391 | 5. If SameValue(*f*, *next*) is **false**, then throw a **TypeError** exception.
392 | 6. If *O* does not have a [[IteratedList]] internal slot, then throw a **TypeError** exception.
393 | 7. Let *list* be the value of the [[IteratedList]] internal slot of *O*.
394 | 8. Let *index* be the value of the [[ListReverseIteratorNextIndex]] internal slot of *O*.
395 | 10. If *index* < 0, then
396 | * a. Return CreateIterResultObject(**undefined**, **true**).
397 | 11. Set the value of the [[ListReverseIteratorNextIndex]] internal slot of *O* to *index*-1.
398 | 12. Return CreateIterResultObject(*list*[*index*], **false**).
399 |
400 | NOTE A ListReverseIterator **next** method will throw an exception if applied to any object other than the one with which it was originally associated.
401 |
402 | #### 7.4.X.2 ListReverseIterator reverse ( )
403 |
404 | The ListReverseIterator **reverse** method is a standard built-in function object (clause 17)
405 | that performs the following steps:
406 |
407 | 1. Let *O* be the **this** value.
408 | 2. If *O* does not have a [[IteratorList]] internal slot, then throw a **TypeError** exception.
409 | 3. Let *list* be the value of the [[IteratorList]] internal slot of *O*.
410 | 4. Return CreateListIterator(*list*).
411 |
412 |
413 | ### 7.4.9 CreateCompoundIterator ( iterator1, iterator2 )
414 | > This existing abstract operation has had 3 new steps added: 8, 9, and 10.
415 |
416 | 1. Let *iterator* be ObjectCreate(%IteratorPrototype%, ([[Iterator1]], [[Iterator2]], [[State]], [[IteratorNext]])).
417 | 2. Set *iterator’s* [[Iterator1]] internal slot to *iterator1*.
418 | 3. Set *iterator’s* [[Iterator2]] internal slot to *iterator2*.
419 | 4. Set *iterator’s* [[State]] internal slot to 1.
420 | 5. Let *next* be a new built-in function object as defined in CompoundIterator **next** (7.4.10.1).
421 | 6. Set *iterator’s* [[IteratorNext]] internal slot to *next*.
422 | 7. Perform CreateMethodProperty(*iterator*, **"next"**, *next*).
423 | 8. Let *usingReverseIterator1* be GetMethod(*iterator1*, @@reverseIterator).
424 | 9. Let *usingReverseIterator2* be GetMethod(*iterator2*, @@reverseIterator).
425 | 10. If *usingReverseIterator1* is not **undefined** and *usingReverseIterator2* is not **undefined**.
426 | * a. Let *reverse* be the built-in function object CompoundIterator **reverse**.
427 | * b. Perform CreateMethodProperty(*iterator*, @@reverseIterator, *reverse*).
428 | 11. Return *iterator*.
429 |
430 | #### 7.4.9.X CompoundIterator reverse ( )
431 | > This method is new
432 |
433 | The CompoundIterator **reverse** method is a standard built-in function object (clause 17)
434 | that performs the following steps:
435 |
436 | 1. Let *O* be the **this** value.
437 | 2. If *O* does not have [[Iterator1]] and [[Iterator2]] internal slots, then throw a **TypeError** exception.
438 | 3. Let *state* be the value of the [[State]] internal slot of *O*.
439 | 4. If *state* is not 1, throw a **TypeError** exception.
440 | 5. Let *iterator1* be the value of the [[Iterator1] internal slot of *O*.
441 | 6. Let *iterator2* be the value of the [[Iterator2] internal slot of *O*.
442 | 7. Let *usingReverseIterator1* be GetMethod(*iterator1*, @@reverseIterator).
443 | 8. If *usingReverseIterator1* is **undefined**, throw a **TypeError** exception.
444 | 9. Let *reverseIterator1* be GetIterator(*O*, *usingReverseIterator1*).
445 | 10. Let *usingReverseIterator2* be GetMethod(*iterator2*, @@reverseIterator).
446 | 11. If *usingReverseIterator2* is **undefined**, throw a **TypeError** exception.
447 | 12. Let *reverseIterator2* be GetIterator(*O*, *usingReverseIterator2*).
448 | 13. Return CreateCompoundIterator(reverseIterator2, reverseIterator1).
449 |
--------------------------------------------------------------------------------
/TC39-March2015.key:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leebyron/ecmascript-reverse-iterable/3d4a10ba11749dfd0d107b75ae8a2206c0de299e/TC39-March2015.key
--------------------------------------------------------------------------------
/TC39-March2015.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leebyron/ecmascript-reverse-iterable/3d4a10ba11749dfd0d107b75ae8a2206c0de299e/TC39-March2015.pdf
--------------------------------------------------------------------------------
/es6.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // The following is all currently specced behavior in ES2015 (ES6).
4 | // It is all either directly referred to in the proposal, or is contextually
5 | // relevant to the proposal in order to produce meaningful examples.
6 |
7 | // 7.2.2
8 | global.IsCallable = function IsCallable(argument) {
9 | return typeof argument === 'function';
10 | };
11 |
12 | // 7.3.5
13 | global.CreateMethodProperty = function CreateMethodProperty(O, P, V) {
14 | return Object.defineProperty(O, P, {
15 | value: V,
16 | writable: true,
17 | enumerable: false,
18 | configurable: true
19 | });
20 | };
21 |
22 | // 7.3.7
23 | global.GetMethod = function GetMethod(O, P) {
24 | // 1. Assert: Type(O) is Object.
25 | if (Object(O) !== O) {
26 | throw new TypeError();
27 | }
28 | // 2. Assert: IsPropertyKey(P) is true.
29 | // 3. Let func be the result of calling the [[Get]] internal method of O passing P and O as the arguments.
30 | // 4. ReturnIfAbrupt(func).
31 | var func = O[P];
32 | // 5. If func is either undefined or null, then return undefined.
33 | if (func === undefined || func === null) {
34 | return undefined;
35 | }
36 | // 6. If IsCallable(func) is false, then throw a TypeError exception.
37 | if (IsCallable(func) === false) {
38 | throw new TypeError();
39 | }
40 | // 7. Return func.
41 | return func;
42 | };
43 |
44 | // 7.4.1
45 | global.GetIterator = function GetIterator(obj, method) {
46 | // 1. ReturnIfAbrupt(obj).
47 | // 2. If method was not passed, then
48 | if (arguments.length < 2) {
49 | // a. Let method be GetMethod(obj, @@iterator).
50 | // b. ReturnIfAbrupt(method).
51 | method = GetMethod(obj, Symbol.iterator);
52 | }
53 | // 3. If IsCallable(method) is false, then throw a TypeError exception.
54 | if (IsCallable(method) === false) {
55 | throw new TypeError('method must be callable');
56 | }
57 | // 4. Let iterator be the result of calling the [[Call]] internal method of
58 | // method with obj as thisArgument and an empty List as argumentsList.
59 | var iterator = method.call(obj);
60 | // 5. ReturnIfAbrupt(iterator).
61 | // 6. If Type(iterator) is not Object, then throw a TypeError exception.
62 | if (Object(iterator) !== iterator) {
63 | throw new TypeError('method must return an iterator');
64 | }
65 | // 7. Return iterator.
66 | return iterator;
67 | };
68 |
69 | // 7.4.7
70 | global.CreateIterResultObject = function CreateIterResultObject(value, done) {
71 | // 1. Assert: Type(done) is Boolean.
72 | // 2. Let obj be ObjectCreate(%ObjectPrototype%).
73 | // 3. Perform CreateDataProperty(obj, "value", value).
74 | // 4. Perform CreateDataProperty(obj, "done", done).
75 | // 5. Return obj.
76 | return { value: value, done: done };
77 | };
78 |
79 | // 9.1.13
80 | global.ObjectCreate = function ObjectCreate(proto, internalSlotsList) {
81 | var properties = {};
82 | if (internalSlotsList) {
83 | for (var ii = 0; ii < internalSlotsList.length; ii++) {
84 | var name = internalSlotsList[ii];
85 | properties[name] = { writable: true, configurable: false, enumerable: false };
86 | }
87 | }
88 | return Object.create(proto, properties);
89 | };
90 |
91 | // 19.4.1
92 | global.Symbol = function Symbol(k) {
93 | return k;
94 | };
95 |
96 | // 19.4.2.5
97 | Symbol.iterator = Symbol('@@iterator');
98 | Symbol.isConcatSpreadable = Symbol('@@isConcatSpreadable');
99 |
100 | // 25.1.2
101 | global.IteratorPrototype = {};
102 |
103 | // 25.1.2.1.1
104 | IteratorPrototype[Symbol.iterator] = function () {
105 | return this;
106 | };
107 |
108 | // 22.1.3.4
109 | CreateMethodProperty(Array.prototype, 'entries', function () {
110 | // 1. Let O be the result of calling ToObject with the this value as its argument.
111 | // 2. ReturnIfAbrupt(O).
112 | var O = Object(this);
113 |
114 | // 3. Return CreateArrayIterator(O, "key+value").
115 | return CreateArrayIterator(O, 'key+value');
116 | });
117 |
118 | // 22.1.3.13
119 | CreateMethodProperty(Array.prototype, 'keys', function () {
120 | // 1. Let O be the result of calling ToObject with the this value as its argument.
121 | // 2. ReturnIfAbrupt(O).
122 | var O = Object(this);
123 |
124 | // 3. Return CreateArrayIterator(O, "key").
125 | return CreateArrayIterator(O, 'key');
126 | });
127 |
128 | // 22.1.3.29
129 | CreateMethodProperty(Array.prototype, 'values', function () {
130 | // 1. Let O be the result of calling ToObject with the this value as its argument.
131 | // 2. ReturnIfAbrupt(O).
132 | var O = Object(this);
133 |
134 | // 3. Return CreateArrayIterator(O, "value").
135 | return CreateArrayIterator(O, 'value');
136 | });
137 |
138 | // 22.1.3.30
139 | CreateMethodProperty(Array.prototype, Symbol.iterator, Array.prototype.values);
140 |
141 | // 22.1.5.1
142 | global.CreateArrayIterator = function CreateArrayIterator(array, kind) {
143 | var iterator = ObjectCreate(
144 | ArrayIteratorPrototype,
145 | ['[[IteratedObject]]', '[[ArrayIteratorNextIndex]]', '[[ArrayIterationKind]]']
146 | );
147 | iterator['[[IteratedObject]]'] = array;
148 | iterator['[[ArrayIteratorNextIndex]]'] = 0;
149 | iterator['[[ArrayIterationKind]]'] = kind;
150 | return iterator;
151 | }
152 |
153 | // 22.1.5.2
154 | global.ArrayIteratorPrototype = ObjectCreate(IteratorPrototype);
155 |
156 | // 22.1.5.2.1
157 | CreateMethodProperty(ArrayIteratorPrototype, 'next', function() {
158 | // 1. Let O be the this value.
159 | var O = this;
160 |
161 | // 2. If Type(O) is not Object, throw a TypeError exception.
162 | if (Object(O) !== O) {
163 | throw new TypeError();
164 | }
165 |
166 | // 3. If O does not have all of the internal slots of an Array Iterator Instance (22.1.5.3), throw a
167 | // TypeError exception.
168 |
169 | // 4. Let a be the value of the [[IteratedObject]] internal slot of O.
170 | var a = O['[[IteratedObject]]'];
171 |
172 | // 5. If a is undefined, then return CreateIterResultObject(undefined, true).
173 | if (a === undefined) {
174 | return CreateIterResultObject(undefined, true);
175 | }
176 |
177 | // 6. Let index be the value of the [[ArrayIteratorNextIndex]] internal slot of O.
178 | var index = O['[[ArrayIteratorNextIndex]]'];
179 |
180 | // 7. Let itemKind be the value of the [[ArrayIterationKind]] internal slot of O.
181 | var itemKind = O['[[ArrayIterationKind]]'];
182 |
183 | // 8. If *array* has a [[TypedArrayName]] internal slot, then
184 | // * a. Let *len* be the value of the [[ArrayLength]] internal slot of *array*.
185 | // 9. Else,
186 | // * a. Let *len* be ToLength(Get(*array*, **"length"**)).
187 | // * b. ReturnIfAbrupt(*len*).
188 | var len = a.length;
189 |
190 | // 10. If index ≥ len, then
191 | if (index >= len) {
192 | // a. Set the value of the [[IteratedObject]] internal slot of O to undefined.
193 | O['[[IteratedObject]]'] = undefined;
194 | // b. Return CreateIterResultObject(undefined, true).
195 | return CreateIterResultObject(undefined, true);
196 | }
197 |
198 | // 11. Set the value of the [[ArrayIteratorNextIndex]] internal slot of O to index+1.
199 | O['[[ArrayIteratorNextIndex]]'] = index + 1;
200 |
201 | var result;
202 |
203 | // 12. If itemKind is "key", then return CreateIterResultObject(index, false);
204 | if (itemKind === 'key') {
205 | return CreateIterResultObject(index, false);
206 | }
207 |
208 | // 13. Let elementKey be ToString(index).
209 | // 14. Let elementValue be Get(a, elementKey).
210 | // 15. ReturnIfAbrupt(elementValue).
211 | var elementValue = a[index];
212 |
213 | // 16. If itemKind is "value", then let result be elementValue.
214 | if (itemKind === 'value') {
215 | result = elementValue;
216 |
217 | // 17. Else,
218 | } else {
219 |
220 | // a. Assert itemKind is "key+value",.
221 | // b. Let result be CreateArrayFromList(«index, elementValue»).
222 | result = [index, elementValue];
223 | }
224 |
225 | // 18. Return CreateIterResultObject(result, false).
226 | return CreateIterResultObject(result, false);
227 | });
228 |
--------------------------------------------------------------------------------
/examples.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | require('./polyfill-spec');
4 |
5 | (function () {
6 | // Array can produce a default reverse iterator with [Symbol.reverseIterator]();
7 | var array = ['A','B','C'];
8 | var rev = array[Symbol.reverseIterator]();
9 | console.log(rev.next()); // "C"
10 | console.log(rev.next()); // "B"
11 | console.log(rev.next()); // "A"
12 | console.log(rev.next()); // undefined
13 | console.log(rev.next()); // undefined
14 | })();
15 |
16 |
17 | (function () {
18 | // Array iterator can produce a reverse iterator of the same kind with reverse()
19 | var array = ['A','B','C'];
20 | var revEntries = array.entries().reverse();
21 | console.log(revEntries.next()); // [2, "C"]
22 | console.log(revEntries.next()); // [1, "B"]
23 | console.log(revEntries.next()); // [0, "A"]
24 | console.log(revEntries.next()); // undefined
25 | console.log(revEntries.next()); // undefined
26 | })();
27 |
28 |
29 | (function () {
30 | // Illustrate example of a user-land `map` function which checks for
31 | // @@reverseIterable to determine if it itself should implement ReverseIterable.
32 |
33 | var IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
34 |
35 | function mapIterator(originalIterator, mapper, context) {
36 | var iterator = Object.create(IteratorPrototype, {
37 | originalIterator: { value: originalIterator, writable: true },
38 | mapper: { value: mapper, writable: true },
39 | context: { value: context, writable: true },
40 | next: { value: MapIteratorNext }
41 | });
42 | var hasReverseIterable = originalIterator[Symbol.reverseIterator];
43 | if (hasReverseIterable) {
44 | iterator[Symbol.reverseIterator] = MapIteratorReversed;
45 | }
46 | return iterator;
47 | }
48 |
49 | function MapIteratorNext() {
50 | var originalIterator = this.originalIterator;
51 | if (originalIterator === undefined) {
52 | return { value: undefined, done: true };
53 | }
54 | var result = originalIterator.next();
55 | if (result.done) {
56 | this.originalIterator = undefined;
57 | return result;
58 | }
59 | return { value: this.mapper.call(this.context, result.value), done: false };
60 | }
61 |
62 | function MapIteratorReversed() {
63 | var originalIterator = this.originalIterator;
64 | var reverseIterator = originalIterator[Symbol.reverseIterator]();
65 | return mapIterator(reverseIterator, this.mapper, this.context);
66 | }
67 |
68 |
69 | var array = ['A','B','C'];
70 |
71 | // Illustrate that a reverse-iterator can be mapped, and the result of that
72 | // can be reversed itself. A reverse-iterator can be reversed yet again.
73 | // This simply sets up the iterator, no buffering occurs.
74 | var rev = mapIterator(array.values().reverse(), function (l) { return l + l; }).reverse().reverse();
75 | console.log(rev.next()); // "CC"
76 | console.log(rev.next()); // "BB"
77 | console.log(rev.next()); // "AA"
78 | console.log(rev.next()); // undefined
79 | console.log(rev.next()); // undefined
80 | })();
81 |
--------------------------------------------------------------------------------
/polyfill-spec.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | require('./es6');
4 |
5 | //////////////////////////////////////////////////////////////////////////////
6 | // //
7 | // -- The following are proposed additions to a future ECMA spec. -- //
8 | // //
9 | //////////////////////////////////////////////////////////////////////////////
10 |
11 |
12 | // -- This interface definition is new, added within 25.1
13 |
14 |
15 | // # The *ReverseIterable* Interface
16 | //
17 | // The *ReverseIterable* interface includes the following property:
18 | //
19 | // | Property | Value | Requirements |
20 | // | `@@reverseIterator` | A zero arguments function that returns an object. | The function returns an object that conforms to the *Iterator* interface. It must iterate through values in the reverse order of `@@iterator` |
21 | //
22 | // NOTE An object should implement the *ReverseIterable* interface only when it
23 | // also implements the *Iterable* interface.
24 |
25 |
26 | // -- This property is new, added after 25.1.2.1.1
27 | CreateMethodProperty(IteratorPrototype, 'reverse', function() {
28 | // 1. Let *O* be the result of calling ToObject with the **this** value as its argument.
29 | // 2. ReturnIfAbrupt(*O*).
30 | var O = Object(this);
31 |
32 | // 3. Let *usingReverseIterator* be GetMethod(*O*, @@reverseIterator).
33 | var usingReverseIterator = GetMethod(O, Symbol.reverseIterator);
34 |
35 | // 4. If *usingReverseIterator* is **undefined**, throw a **TypeError** exception.
36 | if (usingReverseIterator === undefined) {
37 | throw new TypeError('Iterator is not reversable.');
38 | }
39 |
40 | // 5. Let *iterator* be GetIterator(*O*, *usingReverseIterator*).
41 | var iterator = GetIterator(O, usingReverseIterator);
42 |
43 | // 6. return *iterator*.
44 | return iterator;
45 | });
46 |
47 |
48 | // -- This property is new, added after 19.4.2.5
49 | // # Symbol.reverseIterator
50 | Symbol.reverseIterator = Symbol('@@reverseIterator');
51 |
52 |
53 | // -- This property is new, added after 22.1.3.30
54 | // # Array.prototype [ @@reverseIterator ] ( )
55 | CreateMethodProperty(Array.prototype, Symbol.reverseIterator, function () {
56 | // 1. Let O be the result of calling ToObject with the this value as its argument.
57 | // 2. ReturnIfAbrupt(O).
58 | var O = Object(this);
59 |
60 | // 3. Return CreateArrayReverseIterator(O, "value").
61 | return CreateArrayReverseIterator(O, 'value');
62 | });
63 |
64 |
65 | // -- These two properties are added to ArrayIteratorPrototype, 22.1.5.2
66 | // # ArrayIteratorPrototype [ @@reverseIterator ] ( )
67 | CreateMethodProperty(ArrayIteratorPrototype, Symbol.reverseIterator, function () {
68 | // 1. Let *O* be the **this** value.
69 | var O = this;
70 |
71 | // 2. If Type(*O*) is not Object, throw a **TypeError** exception.
72 | if (Object(O) !== O) {
73 | throw new TypeError('must be called on object');
74 | }
75 |
76 | // 3. If *O* does not have all of the internal slots of an Array Iterator Instance, throw a **TypeError** exception.
77 |
78 | // 4. Let *a* be the value of the [[IteratedObject]] internal slot of *O*.
79 | var a = O['[[IteratedObject]]'];
80 |
81 | // 5. Let *index* be the value of the [[ArrayIteratorNextIndex]] internal slot of *O*.
82 | var index = O['[[ArrayIteratorNextIndex]]'];
83 |
84 | // 6. If *index* !== 0, then throw a **TypeError** exception.
85 | if (index !== 0) {
86 | throw new TypeError('Cannot reverse once iteration has begun.');
87 | }
88 |
89 | // 7. Let *itemKind* be the value of the [[ArrayIterationKind]] internal slot of *O*.
90 | var itemKind = O['[[ArrayIterationKind]]'];
91 |
92 | // 8. Return CreateArrayReverseIterator(*a*, *itemKind*).
93 | return CreateArrayReverseIterator(a, itemKind);
94 | });
95 |
96 |
97 |
98 | // -- This section is new, added after 22.1.5
99 |
100 | // # Array Reverse Iterator Objects
101 |
102 | // An Array Reverse Iterator is an object, that represents a specific reverse
103 | // iteration over some specific Array instance object. There is not a named
104 | // constructor for Array Reverse Iterator objects. Instead, Array Reverse
105 | // Iterator objects are created by calling **reverse** on Array Iterator objects.
106 |
107 |
108 | // # CreateArrayReverseIterator Abstract Operation
109 | global.CreateArrayReverseIterator = function CreateArrayReverseIterator(array, kind) {
110 | // 1. Assert: Type(*array*) is Object
111 | if (Object(array) !== array) {
112 | throw new TypeError('array must be Object');
113 | }
114 |
115 | // 2. Let *iterator* be ObjectCreate(%ArrayIteratorPrototype%, ([[IteratedObject]], [[ArrayReverseIteratorNextIndex]], [[ArrayIterationKind]])).
116 | var iterator = ObjectCreate(
117 | ArrayReverseIteratorPrototype,
118 | ['[[IteratedObject]]', '[[ArrayReverseIteratorNextIndex]]', '[[ArrayIterationKind]]']
119 | );
120 |
121 | // 3. Set *iterator’s* [[IteratedObject]] internal slot to *array*.
122 | iterator['[[IteratedObject]]'] = array;
123 |
124 | // 4. If *array* has a [[TypedArrayName]] internal slot, then
125 | // * a. Let *len* be the value of the [[ArrayLength]] internal slot of *array*.
126 | // 5. Else,
127 | // * a. Let *len* be ToLength(Get(*array*, **"length"**)).
128 | // * b. ReturnIfAbrupt(*len*).
129 | var len = array.length;
130 |
131 | // 6. Set *iterator’s* [[ArrayReverseIteratorNextIndex]] internal slot to *len*-1.
132 | iterator['[[ArrayReverseIteratorNextIndex]]'] = len - 1;
133 |
134 | // 7. Set *iterator’s* [[ArrayIteratorKind]] internal slot to *kind*.
135 | iterator['[[ArrayIterationKind]]'] = kind;
136 |
137 | // 8. Return *iterator*.
138 | return iterator;
139 | }
140 |
141 |
142 | // # The %ArrayReverseIteratorPrototype% Object
143 |
144 | // All Array Reverse Iterator Objects inherit properties from the
145 | // %ArrayReverseIteratorPrototype% intrinsic object. The
146 | // %ArrayReverseIteratorPrototype% object is an ordinary object and its
147 | // [[Prototype]] internal slot is the %IteratorPrototype% intrinsic object
148 | // (25.1.2). In addition, %ArrayReverseIteratorPrototype% has the following
149 | // properties:
150 | global.ArrayReverseIteratorPrototype = ObjectCreate(IteratorPrototype);
151 |
152 |
153 | // # %ArrayReverseIteratorPrototype%.next ( )
154 | CreateMethodProperty(ArrayReverseIteratorPrototype, 'next', function () {
155 |
156 | // 1. Let O be the this value.
157 | var O = this;
158 |
159 | // 2. If Type(O) is not Object, throw a TypeError exception.
160 | if (Object(O) !== O) {
161 | throw new TypeError();
162 | }
163 |
164 | // 3. If O does not have all of the internal slots of an Array Iterator Instance (22.1.5.3), throw a
165 | // TypeError exception.
166 |
167 | // 4. Let a be the value of the [[IteratedObject]] internal slot of O.
168 | var a = O['[[IteratedObject]]'];
169 |
170 | // 5. If a is undefined, then return CreateIterResultObject(undefined, true).
171 | if (a === undefined) {
172 | return CreateIterResultObject(undefined, true);
173 | }
174 |
175 | // 6. Let index be the value of the [[ArrayReverseIteratorNextIndex]] internal slot of O.
176 | var index = O['[[ArrayReverseIteratorNextIndex]]'];
177 |
178 | // 7. Let itemKind be the value of the [[ArrayIterationKind]] internal slot of O.
179 | var itemKind = O['[[ArrayIterationKind]]'];
180 |
181 | // 8. If index < 0, then
182 | if (index < 0) {
183 | // a. Set the value of the [[IteratedObject]] internal slot of O to undefined.
184 | O['[[IteratedObject]]'] = undefined;
185 | // b. Return CreateIterResultObject(undefined, true).
186 | return CreateIterResultObject(undefined, true);
187 | }
188 |
189 | // 9. Set the value of the [[ArrayReverseIteratorNextIndex]] internal slot of O to index-1.
190 | O['[[ArrayReverseIteratorNextIndex]]'] = index - 1;
191 |
192 | var result;
193 |
194 | // 10. If itemKind is "key", return CreateIterResultObject(*index*, **false**).
195 | if (itemKind === 'key') {
196 | return CreateIterResultObject(index, false);
197 | }
198 |
199 | // 11. Let elementKey be ToString(index).
200 | // 12. Let elementValue be Get(a, elementKey).
201 | // 13. ReturnIfAbrupt(elementValue).
202 | var elementValue = a[index];
203 |
204 | // 14. If itemKind is "value", then let result be elementValue.
205 | if (itemKind === 'value') {
206 | result = elementValue;
207 |
208 | // 15. Else,
209 | } else {
210 |
211 | // a. Assert *itemKind* is **"key+value"**,.
212 | // b. Let *result* be CreateArrayFromList(*«index, elementValue»*).
213 | result = [index, elementValue];
214 | }
215 |
216 | // 16. Return CreateIterResultObject(result, false).
217 | return CreateIterResultObject(result, false);
218 | });
219 |
220 |
221 | // # ArrayReverseIteratorPrototype [ @@reverseIterator ] ( )
222 | CreateMethodProperty(ArrayReverseIteratorPrototype, Symbol.reverseIterator, function () {
223 | // 1. Let *O* be the **this** value.
224 | var O = this;
225 |
226 | // 2. If Type(O) is not Object, throw a TypeError exception.
227 | if (Object(O) !== O) {
228 | throw new TypeError();
229 | }
230 |
231 | // 3. If O does not have all of the internal slots of an Array Iterator Instance (22.1.5.3), throw a
232 | // TypeError exception.
233 |
234 | // 4. Let *a* be the value of the [[IteratedObject]] internal slot of *O*.
235 | var a = O['[[IteratedObject]]'];
236 |
237 | // 5. Let *index* be the value of the [[ArrayReverseIteratorNextIndex]] internal slot of *O*.
238 | var index = O['[[ArrayReverseIteratorNextIndex]]'];
239 |
240 | // 6. If *a* has a [[TypedArrayName]] internal slot, then
241 | // * a. Let *len* be the value of the [[ArrayLength]] internal slot of *a*.
242 | // 7. Else,
243 | // * a. Let *len* be ToLength(Get(*a*, **"length"**)).
244 | // * b. ReturnIfAbrupt(*len*).
245 | var len = a.length;
246 |
247 | // 8. If *index* !== *len*-1, then throw a **TypeError** exception.
248 | if (index !== len - 1) {
249 | throw new TypeError('Cannot reverse once iteration has begun.');
250 | }
251 |
252 | // 9. Let *itemKind* be the value of the [[ArrayIterationKind]] internal slot of *O*.
253 | var itemKind = O['[[ArrayIterationKind]]'];
254 |
255 | // 10. Return CreateArrayIterator(*a*, *itemKind*).
256 | return CreateArrayIterator(a, itemKind);
257 | });
258 |
259 |
260 |
261 | // TODO: changes in 7.4
262 |
--------------------------------------------------------------------------------