UNPKG

4.66 kBJavaScriptView Raw
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @format
8 */
9
10'use strict';
11
12/**
13 * Given an object `toIterator` will return the itrator for that object. If the
14 * object has a `Symbol.iterator` method we just call that. Otherwise we
15 * implement the ES6 `Array` and `String` Iterator.
16 */
17
18/**
19 * Constants
20 */
21
22const KIND_KEY = 'key';
23const KIND_VALUE = 'value';
24const KIND_KEY_VAL = 'key+value';
25/*global Symbol: true*/
26const ITERATOR_SYMBOL =
27 typeof Symbol === 'function' ? Symbol.iterator : '@@iterator';
28
29const toIterator = (function() {
30 if (
31 !(Array.prototype[ITERATOR_SYMBOL] && String.prototype[ITERATOR_SYMBOL])
32 ) {
33 // IIFE to avoid creating classes for no reason because of hoisting.
34 return (function() {
35 class ArrayIterator {
36 // 22.1.5.1 CreateArrayIterator Abstract Operation
37 constructor(array, kind) {
38 if (!Array.isArray(array)) {
39 throw new TypeError('Object is not an Array');
40 }
41 this._iteratedObject = array;
42 this._kind = kind;
43 this._nextIndex = 0;
44 }
45
46 // 22.1.5.2.1 %ArrayIteratorPrototype%.next()
47 next() {
48 if (!this instanceof ArrayIterator) {
49 throw new TypeError('Object is not an ArrayIterator');
50 }
51
52 if (this._iteratedObject == null) {
53 return createIterResultObject(undefined, true);
54 }
55
56 const array = this._iteratedObject;
57 const len = this._iteratedObject.length;
58 const index = this._nextIndex;
59 const kind = this._kind;
60
61 if (index >= len) {
62 this._iteratedObject = undefined;
63 return createIterResultObject(undefined, true);
64 }
65
66 this._nextIndex = index + 1;
67
68 if (kind === KIND_KEY) {
69 return createIterResultObject(index, false);
70 } else if (kind === KIND_VALUE) {
71 return createIterResultObject(array[index], false);
72 } else if (kind === KIND_KEY_VAL) {
73 return createIterResultObject([index, array[index]], false);
74 }
75 }
76
77 // 22.1.5.2.2 %ArrayIteratorPrototype%[@@iterator]()
78 '@@iterator'() {
79 return this;
80 }
81 }
82
83 class StringIterator {
84 // 21.1.5.1 CreateStringIterator Abstract Operation
85 constructor(string) {
86 if (typeof string !== 'string') {
87 throw new TypeError('Object is not a string');
88 }
89 this._iteratedString = string;
90 this._nextIndex = 0;
91 }
92
93 // 21.1.5.2.1 %StringIteratorPrototype%.next()
94 next() {
95 if (!this instanceof StringIterator) {
96 throw new TypeError('Object is not a StringIterator');
97 }
98
99 if (this._iteratedString == null) {
100 return createIterResultObject(undefined, true);
101 }
102
103 const index = this._nextIndex;
104 const s = this._iteratedString;
105 const len = s.length;
106
107 if (index >= len) {
108 this._iteratedString = undefined;
109 return createIterResultObject(undefined, true);
110 }
111
112 let ret;
113 const first = s.charCodeAt(index);
114
115 if (first < 0xd800 || first > 0xdbff || index + 1 === len) {
116 ret = s[index];
117 } else {
118 const second = s.charCodeAt(index + 1);
119 if (second < 0xdc00 || second > 0xdfff) {
120 ret = s[index];
121 } else {
122 ret = s[index] + s[index + 1];
123 }
124 }
125
126 this._nextIndex = index + ret.length;
127
128 return createIterResultObject(ret, false);
129 }
130
131 // 21.1.5.2.2 %StringIteratorPrototype%[@@ITERATOR_SYMBOL]()
132 '@@iterator'() {
133 return this;
134 }
135 }
136
137 // 7.4.7 createIterResultObject(value, done)
138 function createIterResultObject(value, done) {
139 return {value: value, done: done};
140 }
141
142 return function(object, kind) {
143 if (typeof object === 'string') {
144 return new StringIterator(object);
145 } else if (Array.isArray(object)) {
146 return new ArrayIterator(object, kind || KIND_VALUE);
147 } else {
148 return object[ITERATOR_SYMBOL]();
149 }
150 };
151 })();
152 } else {
153 return function(object) {
154 return object[ITERATOR_SYMBOL]();
155 };
156 }
157})();
158
159/**
160 * Export constants
161 */
162
163Object.assign(toIterator, {
164 KIND_KEY,
165 KIND_VALUE,
166 KIND_KEY_VAL,
167 ITERATOR_SYMBOL,
168});
169
170module.exports = toIterator;