baseClone.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. var arrayCopy = require('./arrayCopy'),
  2. arrayEach = require('./arrayEach'),
  3. baseAssign = require('./baseAssign'),
  4. baseForOwn = require('./baseForOwn'),
  5. initCloneArray = require('./initCloneArray'),
  6. initCloneByTag = require('./initCloneByTag'),
  7. initCloneObject = require('./initCloneObject'),
  8. isArray = require('../lang/isArray'),
  9. isObject = require('../lang/isObject');
  10. /** `Object#toString` result references. */
  11. var argsTag = '[object Arguments]',
  12. arrayTag = '[object Array]',
  13. boolTag = '[object Boolean]',
  14. dateTag = '[object Date]',
  15. errorTag = '[object Error]',
  16. funcTag = '[object Function]',
  17. mapTag = '[object Map]',
  18. numberTag = '[object Number]',
  19. objectTag = '[object Object]',
  20. regexpTag = '[object RegExp]',
  21. setTag = '[object Set]',
  22. stringTag = '[object String]',
  23. weakMapTag = '[object WeakMap]';
  24. var arrayBufferTag = '[object ArrayBuffer]',
  25. float32Tag = '[object Float32Array]',
  26. float64Tag = '[object Float64Array]',
  27. int8Tag = '[object Int8Array]',
  28. int16Tag = '[object Int16Array]',
  29. int32Tag = '[object Int32Array]',
  30. uint8Tag = '[object Uint8Array]',
  31. uint8ClampedTag = '[object Uint8ClampedArray]',
  32. uint16Tag = '[object Uint16Array]',
  33. uint32Tag = '[object Uint32Array]';
  34. /** Used to identify `toStringTag` values supported by `_.clone`. */
  35. var cloneableTags = {};
  36. cloneableTags[argsTag] = cloneableTags[arrayTag] =
  37. cloneableTags[arrayBufferTag] = cloneableTags[boolTag] =
  38. cloneableTags[dateTag] = cloneableTags[float32Tag] =
  39. cloneableTags[float64Tag] = cloneableTags[int8Tag] =
  40. cloneableTags[int16Tag] = cloneableTags[int32Tag] =
  41. cloneableTags[numberTag] = cloneableTags[objectTag] =
  42. cloneableTags[regexpTag] = cloneableTags[stringTag] =
  43. cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
  44. cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
  45. cloneableTags[errorTag] = cloneableTags[funcTag] =
  46. cloneableTags[mapTag] = cloneableTags[setTag] =
  47. cloneableTags[weakMapTag] = false;
  48. /** Used for native method references. */
  49. var objectProto = Object.prototype;
  50. /**
  51. * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
  52. * of values.
  53. */
  54. var objToString = objectProto.toString;
  55. /**
  56. * The base implementation of `_.clone` without support for argument juggling
  57. * and `this` binding `customizer` functions.
  58. *
  59. * @private
  60. * @param {*} value The value to clone.
  61. * @param {boolean} [isDeep] Specify a deep clone.
  62. * @param {Function} [customizer] The function to customize cloning values.
  63. * @param {string} [key] The key of `value`.
  64. * @param {Object} [object] The object `value` belongs to.
  65. * @param {Array} [stackA=[]] Tracks traversed source objects.
  66. * @param {Array} [stackB=[]] Associates clones with source counterparts.
  67. * @returns {*} Returns the cloned value.
  68. */
  69. function baseClone(value, isDeep, customizer, key, object, stackA, stackB) {
  70. var result;
  71. if (customizer) {
  72. result = object ? customizer(value, key, object) : customizer(value);
  73. }
  74. if (result !== undefined) {
  75. return result;
  76. }
  77. if (!isObject(value)) {
  78. return value;
  79. }
  80. var isArr = isArray(value);
  81. if (isArr) {
  82. result = initCloneArray(value);
  83. if (!isDeep) {
  84. return arrayCopy(value, result);
  85. }
  86. } else {
  87. var tag = objToString.call(value),
  88. isFunc = tag == funcTag;
  89. if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
  90. result = initCloneObject(isFunc ? {} : value);
  91. if (!isDeep) {
  92. return baseAssign(result, value);
  93. }
  94. } else {
  95. return cloneableTags[tag]
  96. ? initCloneByTag(value, tag, isDeep)
  97. : (object ? value : {});
  98. }
  99. }
  100. // Check for circular references and return its corresponding clone.
  101. stackA || (stackA = []);
  102. stackB || (stackB = []);
  103. var length = stackA.length;
  104. while (length--) {
  105. if (stackA[length] == value) {
  106. return stackB[length];
  107. }
  108. }
  109. // Add the source value to the stack of traversed objects and associate it with its clone.
  110. stackA.push(value);
  111. stackB.push(result);
  112. // Recursively populate clone (susceptible to call stack limits).
  113. (isArr ? arrayEach : baseForOwn)(value, function(subValue, key) {
  114. result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB);
  115. });
  116. return result;
  117. }
  118. module.exports = baseClone;