├── 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 | --------------------------------------------------------------------------------