171 lines
4.5 KiB
JavaScript
171 lines
4.5 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @format
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
/**
|
|
* Given an object `toIterator` will return the itrator for that object. If the
|
|
* object has a `Symbol.iterator` method we just call that. Otherwise we
|
|
* implement the ES6 `Array` and `String` Iterator.
|
|
*/
|
|
|
|
/**
|
|
* Constants
|
|
*/
|
|
|
|
const KIND_KEY = 'key';
|
|
const KIND_VALUE = 'value';
|
|
const KIND_KEY_VAL = 'key+value';
|
|
/*global Symbol: true*/
|
|
const ITERATOR_SYMBOL =
|
|
typeof Symbol === 'function' ? Symbol.iterator : '@@iterator';
|
|
|
|
const toIterator = (function() {
|
|
if (
|
|
!(Array.prototype[ITERATOR_SYMBOL] && String.prototype[ITERATOR_SYMBOL])
|
|
) {
|
|
// IIFE to avoid creating classes for no reason because of hoisting.
|
|
return (function() {
|
|
class ArrayIterator {
|
|
// 22.1.5.1 CreateArrayIterator Abstract Operation
|
|
constructor(array, kind) {
|
|
if (!Array.isArray(array)) {
|
|
throw new TypeError('Object is not an Array');
|
|
}
|
|
this._iteratedObject = array;
|
|
this._kind = kind;
|
|
this._nextIndex = 0;
|
|
}
|
|
|
|
// 22.1.5.2.1 %ArrayIteratorPrototype%.next()
|
|
next() {
|
|
if (!this instanceof ArrayIterator) {
|
|
throw new TypeError('Object is not an ArrayIterator');
|
|
}
|
|
|
|
if (this._iteratedObject == null) {
|
|
return createIterResultObject(undefined, true);
|
|
}
|
|
|
|
const array = this._iteratedObject;
|
|
const len = this._iteratedObject.length;
|
|
const index = this._nextIndex;
|
|
const kind = this._kind;
|
|
|
|
if (index >= len) {
|
|
this._iteratedObject = undefined;
|
|
return createIterResultObject(undefined, true);
|
|
}
|
|
|
|
this._nextIndex = index + 1;
|
|
|
|
if (kind === KIND_KEY) {
|
|
return createIterResultObject(index, false);
|
|
} else if (kind === KIND_VALUE) {
|
|
return createIterResultObject(array[index], false);
|
|
} else if (kind === KIND_KEY_VAL) {
|
|
return createIterResultObject([index, array[index]], false);
|
|
}
|
|
}
|
|
|
|
// 22.1.5.2.2 %ArrayIteratorPrototype%[@@iterator]()
|
|
'@@iterator'() {
|
|
return this;
|
|
}
|
|
}
|
|
|
|
class StringIterator {
|
|
// 21.1.5.1 CreateStringIterator Abstract Operation
|
|
constructor(string) {
|
|
if (typeof string !== 'string') {
|
|
throw new TypeError('Object is not a string');
|
|
}
|
|
this._iteratedString = string;
|
|
this._nextIndex = 0;
|
|
}
|
|
|
|
// 21.1.5.2.1 %StringIteratorPrototype%.next()
|
|
next() {
|
|
if (!this instanceof StringIterator) {
|
|
throw new TypeError('Object is not a StringIterator');
|
|
}
|
|
|
|
if (this._iteratedString == null) {
|
|
return createIterResultObject(undefined, true);
|
|
}
|
|
|
|
const index = this._nextIndex;
|
|
const s = this._iteratedString;
|
|
const len = s.length;
|
|
|
|
if (index >= len) {
|
|
this._iteratedString = undefined;
|
|
return createIterResultObject(undefined, true);
|
|
}
|
|
|
|
let ret;
|
|
const first = s.charCodeAt(index);
|
|
|
|
if (first < 0xd800 || first > 0xdbff || index + 1 === len) {
|
|
ret = s[index];
|
|
} else {
|
|
const second = s.charCodeAt(index + 1);
|
|
if (second < 0xdc00 || second > 0xdfff) {
|
|
ret = s[index];
|
|
} else {
|
|
ret = s[index] + s[index + 1];
|
|
}
|
|
}
|
|
|
|
this._nextIndex = index + ret.length;
|
|
|
|
return createIterResultObject(ret, false);
|
|
}
|
|
|
|
// 21.1.5.2.2 %StringIteratorPrototype%[@@ITERATOR_SYMBOL]()
|
|
'@@iterator'() {
|
|
return this;
|
|
}
|
|
}
|
|
|
|
// 7.4.7 createIterResultObject(value, done)
|
|
function createIterResultObject(value, done) {
|
|
return {value: value, done: done};
|
|
}
|
|
|
|
return function(object, kind) {
|
|
if (typeof object === 'string') {
|
|
return new StringIterator(object);
|
|
} else if (Array.isArray(object)) {
|
|
return new ArrayIterator(object, kind || KIND_VALUE);
|
|
} else {
|
|
return object[ITERATOR_SYMBOL]();
|
|
}
|
|
};
|
|
})();
|
|
} else {
|
|
return function(object) {
|
|
return object[ITERATOR_SYMBOL]();
|
|
};
|
|
}
|
|
})();
|
|
|
|
/**
|
|
* Export constants
|
|
*/
|
|
|
|
Object.assign(toIterator, {
|
|
KIND_KEY,
|
|
KIND_VALUE,
|
|
KIND_KEY_VAL,
|
|
ITERATOR_SYMBOL,
|
|
});
|
|
|
|
module.exports = toIterator;
|