enumerate.js.flow 6.7 KB

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