createArrayFromMixed.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. "use strict";
  2. /**
  3. * Copyright (c) 2013-present, Facebook, Inc.
  4. *
  5. * This source code is licensed under the MIT license found in the
  6. * LICENSE file in the root directory of this source tree.
  7. *
  8. * @typechecks
  9. */
  10. var invariant = require("./invariant");
  11. /**
  12. * Convert array-like objects to arrays.
  13. *
  14. * This API assumes the caller knows the contents of the data type. For less
  15. * well defined inputs use createArrayFromMixed.
  16. *
  17. * @param {object|function|filelist} obj
  18. * @return {array}
  19. */
  20. function toArray(obj) {
  21. var length = obj.length; // Some browsers builtin objects can report typeof 'function' (e.g. NodeList
  22. // in old versions of Safari).
  23. !(!Array.isArray(obj) && (typeof obj === 'object' || typeof obj === 'function')) ? process.env.NODE_ENV !== "production" ? invariant(false, 'toArray: Array-like object expected') : invariant(false) : void 0;
  24. !(typeof length === 'number') ? process.env.NODE_ENV !== "production" ? invariant(false, 'toArray: Object needs a length property') : invariant(false) : void 0;
  25. !(length === 0 || length - 1 in obj) ? process.env.NODE_ENV !== "production" ? invariant(false, 'toArray: Object should have keys for indices') : invariant(false) : void 0;
  26. !(typeof obj.callee !== 'function') ? process.env.NODE_ENV !== "production" ? invariant(false, 'toArray: Object can\'t be `arguments`. Use rest params ' + '(function(...args) {}) or Array.from() instead.') : invariant(false) : void 0; // Old IE doesn't give collections access to hasOwnProperty. Assume inputs
  27. // without method will throw during the slice call and skip straight to the
  28. // fallback.
  29. if (obj.hasOwnProperty) {
  30. try {
  31. return Array.prototype.slice.call(obj);
  32. } catch (e) {// IE < 9 does not support Array#slice on collections objects
  33. }
  34. } // Fall back to copying key by key. This assumes all keys have a value,
  35. // so will not preserve sparsely populated inputs.
  36. var ret = Array(length);
  37. for (var ii = 0; ii < length; ii++) {
  38. ret[ii] = obj[ii];
  39. }
  40. return ret;
  41. }
  42. /**
  43. * Perform a heuristic test to determine if an object is "array-like".
  44. *
  45. * A monk asked Joshu, a Zen master, "Has a dog Buddha nature?"
  46. * Joshu replied: "Mu."
  47. *
  48. * This function determines if its argument has "array nature": it returns
  49. * true if the argument is an actual array, an `arguments' object, or an
  50. * HTMLCollection (e.g. node.childNodes or node.getElementsByTagName()).
  51. *
  52. * It will return false for other array-like objects like Filelist.
  53. *
  54. * @param {*} obj
  55. * @return {boolean}
  56. */
  57. function hasArrayNature(obj) {
  58. return (// not null/false
  59. !!obj && ( // arrays are objects, NodeLists are functions in Safari
  60. typeof obj == 'object' || typeof obj == 'function') && // quacks like an array
  61. 'length' in obj && // not window
  62. !('setInterval' in obj) && // no DOM node should be considered an array-like
  63. // a 'select' element has 'length' and 'item' properties on IE8
  64. typeof obj.nodeType != 'number' && ( // a real array
  65. Array.isArray(obj) || // arguments
  66. 'callee' in obj || // HTMLCollection/NodeList
  67. 'item' in obj)
  68. );
  69. }
  70. /**
  71. * Ensure that the argument is an array by wrapping it in an array if it is not.
  72. * Creates a copy of the argument if it is already an array.
  73. *
  74. * This is mostly useful idiomatically:
  75. *
  76. * var createArrayFromMixed = require('createArrayFromMixed');
  77. *
  78. * function takesOneOrMoreThings(things) {
  79. * things = createArrayFromMixed(things);
  80. * ...
  81. * }
  82. *
  83. * This allows you to treat `things' as an array, but accept scalars in the API.
  84. *
  85. * If you need to convert an array-like object, like `arguments`, into an array
  86. * use toArray instead.
  87. *
  88. * @param {*} obj
  89. * @return {array}
  90. */
  91. function createArrayFromMixed(obj) {
  92. if (!hasArrayNature(obj)) {
  93. return [obj];
  94. } else if (Array.isArray(obj)) {
  95. return obj.slice();
  96. } else {
  97. return toArray(obj);
  98. }
  99. }
  100. module.exports = createArrayFromMixed;