UNPKG

5.58 kBJavaScriptView Raw
1var Stack = require('./_Stack'),
2 arrayEach = require('./_arrayEach'),
3 assignValue = require('./_assignValue'),
4 baseAssign = require('./_baseAssign'),
5 baseAssignIn = require('./_baseAssignIn'),
6 cloneBuffer = require('./_cloneBuffer'),
7 copyArray = require('./_copyArray'),
8 copySymbols = require('./_copySymbols'),
9 copySymbolsIn = require('./_copySymbolsIn'),
10 getAllKeys = require('./_getAllKeys'),
11 getAllKeysIn = require('./_getAllKeysIn'),
12 getTag = require('./_getTag'),
13 initCloneArray = require('./_initCloneArray'),
14 initCloneByTag = require('./_initCloneByTag'),
15 initCloneObject = require('./_initCloneObject'),
16 isArray = require('./isArray'),
17 isBuffer = require('./isBuffer'),
18 isMap = require('./isMap'),
19 isObject = require('./isObject'),
20 isSet = require('./isSet'),
21 keys = require('./keys');
22
23/** Used to compose bitmasks for cloning. */
24var CLONE_DEEP_FLAG = 1,
25 CLONE_FLAT_FLAG = 2,
26 CLONE_SYMBOLS_FLAG = 4;
27
28/** `Object#toString` result references. */
29var argsTag = '[object Arguments]',
30 arrayTag = '[object Array]',
31 boolTag = '[object Boolean]',
32 dateTag = '[object Date]',
33 errorTag = '[object Error]',
34 funcTag = '[object Function]',
35 genTag = '[object GeneratorFunction]',
36 mapTag = '[object Map]',
37 numberTag = '[object Number]',
38 objectTag = '[object Object]',
39 regexpTag = '[object RegExp]',
40 setTag = '[object Set]',
41 stringTag = '[object String]',
42 symbolTag = '[object Symbol]',
43 weakMapTag = '[object WeakMap]';
44
45var arrayBufferTag = '[object ArrayBuffer]',
46 dataViewTag = '[object DataView]',
47 float32Tag = '[object Float32Array]',
48 float64Tag = '[object Float64Array]',
49 int8Tag = '[object Int8Array]',
50 int16Tag = '[object Int16Array]',
51 int32Tag = '[object Int32Array]',
52 uint8Tag = '[object Uint8Array]',
53 uint8ClampedTag = '[object Uint8ClampedArray]',
54 uint16Tag = '[object Uint16Array]',
55 uint32Tag = '[object Uint32Array]';
56
57/** Used to identify `toStringTag` values supported by `_.clone`. */
58var cloneableTags = {};
59cloneableTags[argsTag] = cloneableTags[arrayTag] =
60cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
61cloneableTags[boolTag] = cloneableTags[dateTag] =
62cloneableTags[float32Tag] = cloneableTags[float64Tag] =
63cloneableTags[int8Tag] = cloneableTags[int16Tag] =
64cloneableTags[int32Tag] = cloneableTags[mapTag] =
65cloneableTags[numberTag] = cloneableTags[objectTag] =
66cloneableTags[regexpTag] = cloneableTags[setTag] =
67cloneableTags[stringTag] = cloneableTags[symbolTag] =
68cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
69cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
70cloneableTags[errorTag] = cloneableTags[funcTag] =
71cloneableTags[weakMapTag] = false;
72
73/**
74 * The base implementation of `_.clone` and `_.cloneDeep` which tracks
75 * traversed objects.
76 *
77 * @private
78 * @param {*} value The value to clone.
79 * @param {boolean} bitmask The bitmask flags.
80 * 1 - Deep clone
81 * 2 - Flatten inherited properties
82 * 4 - Clone symbols
83 * @param {Function} [customizer] The function to customize cloning.
84 * @param {string} [key] The key of `value`.
85 * @param {Object} [object] The parent object of `value`.
86 * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
87 * @returns {*} Returns the cloned value.
88 */
89function baseClone(value, bitmask, customizer, key, object, stack) {
90 var result,
91 isDeep = bitmask & CLONE_DEEP_FLAG,
92 isFlat = bitmask & CLONE_FLAT_FLAG,
93 isFull = bitmask & CLONE_SYMBOLS_FLAG;
94
95 if (customizer) {
96 result = object ? customizer(value, key, object, stack) : customizer(value);
97 }
98 if (result !== undefined) {
99 return result;
100 }
101 if (!isObject(value)) {
102 return value;
103 }
104 var isArr = isArray(value);
105 if (isArr) {
106 result = initCloneArray(value);
107 if (!isDeep) {
108 return copyArray(value, result);
109 }
110 } else {
111 var tag = getTag(value),
112 isFunc = tag == funcTag || tag == genTag;
113
114 if (isBuffer(value)) {
115 return cloneBuffer(value, isDeep);
116 }
117 if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
118 result = (isFlat || isFunc) ? {} : initCloneObject(value);
119 if (!isDeep) {
120 return isFlat
121 ? copySymbolsIn(value, baseAssignIn(result, value))
122 : copySymbols(value, baseAssign(result, value));
123 }
124 } else {
125 if (!cloneableTags[tag]) {
126 return object ? value : {};
127 }
128 result = initCloneByTag(value, tag, isDeep);
129 }
130 }
131 // Check for circular references and return its corresponding clone.
132 stack || (stack = new Stack);
133 var stacked = stack.get(value);
134 if (stacked) {
135 return stacked;
136 }
137 stack.set(value, result);
138
139 if (isSet(value)) {
140 value.forEach(function(subValue) {
141 result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
142 });
143 } else if (isMap(value)) {
144 value.forEach(function(subValue, key) {
145 result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
146 });
147 }
148
149 var keysFunc = isFull
150 ? (isFlat ? getAllKeysIn : getAllKeys)
151 : (isFlat ? keysIn : keys);
152
153 var props = isArr ? undefined : keysFunc(value);
154 arrayEach(props || value, function(subValue, key) {
155 if (props) {
156 key = subValue;
157 subValue = value[key];
158 }
159 // Recursively populate clone (susceptible to call stack limits).
160 assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
161 });
162 return result;
163}
164
165module.exports = baseClone;