From 8c66bab8aa8885a502bb9fed59efed7b3a1d7f4c Mon Sep 17 00:00:00 2001 From: Scott Kyle Date: Sat, 19 Mar 2016 19:55:19 -0700 Subject: [PATCH] Make collection iterators actually iterable The ES6 spec calls for this so these iterators are usable in for-of loops, with spread operators, etc. --- lib/array-methods.js | 60 ++++++++++++++++++++++++++---------------- tests/js/list-tests.js | 22 +++++++++------- 2 files changed, 50 insertions(+), 32 deletions(-) diff --git a/lib/array-methods.js b/lib/array-methods.js index ea279122..4d9f29e0 100644 --- a/lib/array-methods.js +++ b/lib/array-methods.js @@ -20,6 +20,19 @@ var arrayPrototype = Array.prototype; +// eslint-disable-next-line no-undef +var iteratorSymbol = typeof Symbol != 'undefined' && Symbol.iterator; +var iteratorPrototype = {}; + +if (iteratorSymbol) { + // These iterators should themselves be iterable. + Object.defineProperty(iteratorPrototype, iteratorSymbol, { + value: function() { + return this; + } + }); +} + [ 'join', 'slice', @@ -43,35 +56,36 @@ var arrayPrototype = Array.prototype; var self = this; var index = 0; - return { - next: function() { - if (!self || index >= self.length) { - self = null; - return {done: true, value: undefined}; - } + return Object.create(iteratorPrototype, { + next: { + value: function() { + if (!self || index >= self.length) { + self = null; + return {done: true, value: undefined}; + } - var value; - switch (methodName) { - case 'entries': - value = [index, self[index]]; - break; - case 'keys': - value = index; - break; - default: - value = self[index]; - } + var value; + switch (methodName) { + case 'entries': + value = [index, self[index]]; + break; + case 'keys': + value = index; + break; + default: + value = self[index]; + } - index++; - return {done: false, value: value}; + index++; + return {done: false, value: value}; + } } - }; + }); }; exports[methodName] = {value: method}; }); -/* global Symbol */ -if (typeof Symbol != 'undefined' && Symbol.iterator) { - exports[Symbol.iterator] = exports.values; +if (iteratorSymbol) { + exports[iteratorSymbol] = exports.values; } diff --git a/tests/js/list-tests.js b/tests/js/list-tests.js index 2d9a66d9..cb316b31 100644 --- a/tests/js/list-tests.js +++ b/tests/js/list-tests.js @@ -576,20 +576,24 @@ module.exports = BaseTest.extend({ TestCase.assertEqual(list.reduce(function(n, p) {return n + p.age}, 0), 33); TestCase.assertEqual(list.reduceRight(function(n, p) {return n + p.age}, 0), 33); - [ - 'entries', - 'keys', - 'values', - typeof Symbol != 'undefined' && Symbol.iterator, // eslint-disable-line no-undef - ].forEach(function(methodName) { - if (!methodName) { - return; - } + // eslint-disable-next-line no-undef + var iteratorSymbol = typeof Symbol != 'undefined' && Symbol.iterator; + var iteratorMethodNames = ['entries', 'keys', 'values']; + if (iteratorSymbol) { + iteratorMethodNames.push(iteratorSymbol); + } + + iteratorMethodNames.forEach(function(methodName) { var iterator = list[methodName](); var count = 0; var result; + if (iteratorSymbol) { + // This iterator should itself be iterable. + TestCase.assertEqual(iterator[iteratorSymbol](), iterator); + } + while ((result = iterator.next()) && !result.done) { var value = result.value;