_baseClone.js
  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;