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