enumerate.js.flow 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /**
  2. * Copyright (c) 2013-present, Facebook, Inc.
  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. * @providesModule enumerate
  8. *
  9. */
  10. const KIND_KEYS = 'keys';
  11. const KIND_VALUES = 'values';
  12. const KIND_ENTRIES = 'entries';
  13. /**
  14. * Specific Array iterators.
  15. */
  16. const ArrayIterators = function () {
  17. let hasNative = hasNativeIterator(Array);
  18. let ArrayIterator;
  19. if (!hasNative) {
  20. ArrayIterator = class ArrayIterator {
  21. // 22.1.5.1 CreateArrayIterator Abstract Operation
  22. constructor(array, kind) {
  23. this._iteratedObject = array;
  24. this._kind = kind;
  25. this._nextIndex = 0;
  26. }
  27. // 22.1.5.2.1 %ArrayIteratorPrototype%.next()
  28. next() {
  29. if (this._iteratedObject == null) {
  30. return { value: undefined, done: true };
  31. }
  32. let array = this._iteratedObject;
  33. let len = this._iteratedObject.length;
  34. let index = this._nextIndex;
  35. let kind = this._kind;
  36. if (index >= len) {
  37. this._iteratedObject = undefined;
  38. return { value: undefined, done: true };
  39. }
  40. this._nextIndex = index + 1;
  41. if (kind === KIND_KEYS) {
  42. return { value: index, done: false };
  43. } else if (kind === KIND_VALUES) {
  44. return { value: array[index], done: false };
  45. } else if (kind === KIND_ENTRIES) {
  46. return { value: [index, array[index]], done: false };
  47. }
  48. }
  49. // 22.1.5.2.2 %ArrayIteratorPrototype%[@@iterator]()
  50. [Symbol.iterator]() {
  51. return this;
  52. }
  53. };
  54. }
  55. return {
  56. keys: hasNative ? array => array.keys() : array => new ArrayIterator(array, KIND_KEYS),
  57. values: hasNative ? array => array.values() : array => new ArrayIterator(array, KIND_VALUES),
  58. entries: hasNative ? array => array.entries() : array => new ArrayIterator(array, KIND_ENTRIES)
  59. };
  60. }();
  61. // -----------------------------------------------------------------
  62. /**
  63. * Specific String iterators.
  64. */
  65. const StringIterators = function () {
  66. let hasNative = hasNativeIterator(String);
  67. let StringIterator;
  68. if (!hasNative) {
  69. StringIterator = class StringIterator {
  70. // 21.1.5.1 CreateStringIterator Abstract Operation
  71. constructor(string) {
  72. this._iteratedString = string;
  73. this._nextIndex = 0;
  74. }
  75. // 21.1.5.2.1 %StringIteratorPrototype%.next()
  76. next() {
  77. if (this._iteratedString == null) {
  78. return { value: undefined, done: true };
  79. }
  80. let index = this._nextIndex;
  81. let s = this._iteratedString;
  82. let len = s.length;
  83. if (index >= len) {
  84. this._iteratedString = undefined;
  85. return { value: undefined, done: true };
  86. }
  87. let ret;
  88. let first = s.charCodeAt(index);
  89. if (first < 0xD800 || first > 0xDBFF || index + 1 === len) {
  90. ret = s[index];
  91. } else {
  92. let second = s.charCodeAt(index + 1);
  93. if (second < 0xDC00 || second > 0xDFFF) {
  94. ret = s[index];
  95. } else {
  96. ret = s[index] + s[index + 1];
  97. }
  98. }
  99. this._nextIndex = index + ret.length;
  100. return { value: ret, done: false };
  101. }
  102. // 21.1.5.2.2 %StringIteratorPrototype%[@@iterator]()
  103. [Symbol.iterator]() {
  104. return this;
  105. }
  106. };
  107. }
  108. return {
  109. keys() {
  110. throw TypeError(`Strings default iterator doesn't implement keys.`);
  111. },
  112. values: hasNative ? string => string[Symbol.iterator]() : string => new StringIterator(string),
  113. entries() {
  114. throw TypeError(`Strings default iterator doesn't implement entries.`);
  115. }
  116. };
  117. }();
  118. function hasNativeIterator(classObject) {
  119. return typeof classObject.prototype[Symbol.iterator] === 'function' && typeof classObject.prototype.values === 'function' && typeof classObject.prototype.keys === 'function' && typeof classObject.prototype.entries === 'function';
  120. }
  121. // -----------------------------------------------------------------
  122. /**
  123. * Generic object iterator.
  124. */
  125. class ObjectIterator {
  126. constructor(object, kind) {
  127. this._iteratedObject = object;
  128. this._kind = kind;
  129. this._keys = Object.keys(object);
  130. this._nextIndex = 0;
  131. }
  132. next() {
  133. let len = this._keys.length;
  134. let index = this._nextIndex;
  135. let kind = this._kind;
  136. let key = this._keys[index];
  137. if (index >= len) {
  138. this._iteratedObject = undefined;
  139. return { value: undefined, done: true };
  140. }
  141. this._nextIndex = index + 1;
  142. if (kind === KIND_KEYS) {
  143. return { value: key, done: false };
  144. } else if (kind === KIND_VALUES) {
  145. return { value: this._iteratedObject[key], done: false };
  146. } else if (kind === KIND_ENTRIES) {
  147. return { value: [key, this._iteratedObject[key]], done: false };
  148. }
  149. }
  150. [Symbol.iterator]() {
  151. return this;
  152. }
  153. }
  154. /**
  155. * Generic object iterator, iterates over all own enumerable
  156. * properties. Used only if if no specific iterator is available,
  157. * and object don't implement iterator protocol.
  158. */
  159. const GenericIterators = {
  160. keys(object) {
  161. return new ObjectIterator(object, KIND_KEYS);
  162. },
  163. values(object) {
  164. return new ObjectIterator(object, KIND_VALUES);
  165. },
  166. entries(object) {
  167. return new ObjectIterator(object, KIND_ENTRIES);
  168. }
  169. };
  170. // -----------------------------------------------------------------
  171. /**
  172. * Main iterator function. Returns default iterator based
  173. * on the class of an instance.
  174. */
  175. function enumerate(object, kind) {
  176. // First check specific iterators.
  177. if (typeof object === 'string') {
  178. return StringIterators[kind || KIND_VALUES](object);
  179. } else if (Array.isArray(object)) {
  180. return ArrayIterators[kind || KIND_VALUES](object);
  181. // Then see if an object implements own.
  182. } else if (object[Symbol.iterator]) {
  183. return object[Symbol.iterator]();
  184. // And fallback to generic with entries.
  185. } else {
  186. return GenericIterators[kind || KIND_ENTRIES](object);
  187. }
  188. }
  189. Object.assign(enumerate, {
  190. /**
  191. * Export constants
  192. */
  193. KIND_KEYS,
  194. KIND_VALUES,
  195. KIND_ENTRIES,
  196. /**
  197. * Convenient explicit iterators for special kinds.
  198. */
  199. keys(object) {
  200. return enumerate(object, KIND_KEYS);
  201. },
  202. values(object) {
  203. return enumerate(object, KIND_VALUES);
  204. },
  205. entries(object) {
  206. return enumerate(object, KIND_ENTRIES);
  207. },
  208. generic: GenericIterators.entries
  209. });
  210. module.exports = enumerate;