import Stack from './Stack.js' import arrayEach from './arrayEach.js' import assignValue from './assignValue.js' import cloneBuffer from './cloneBuffer.js' import copyArray from './copyArray.js' import copyObject from './copyObject.js' import cloneArrayBuffer from './cloneArrayBuffer.js' import cloneDataView from './cloneDataView.js' import cloneRegExp from './cloneRegExp.js' import cloneSymbol from './cloneSymbol.js' import cloneTypedArray from './cloneTypedArray.js' import copySymbols from './copySymbols.js' import copySymbolsIn from './copySymbolsIn.js' import getAllKeys from './getAllKeys.js' import getAllKeysIn from './getAllKeysIn.js' import getTag from './getTag.js' import initCloneObject from './initCloneObject.js' import isBuffer from './isBuffer.js' import isObject from './isObject.js' import isTypedArray from './isTypedArray.js' import keys from './keys.js' import keysIn from './keysIn.js' /** Used to compose bitmasks for cloning. */ /** 用于为克隆合成位掩码。 */ const CLONE_DEEP_FLAG = 1 const CLONE_FLAT_FLAG = 2 const CLONE_SYMBOLS_FLAG = 4 /** `Object#toString` result references. */ const argsTag = '[object Arguments]' const arrayTag = '[object Array]' const boolTag = '[object Boolean]' const dateTag = '[object Date]' const errorTag = '[object Error]' const mapTag = '[object Map]' const numberTag = '[object Number]' const objectTag = '[object Object]' const regexpTag = '[object RegExp]' const setTag = '[object Set]' const stringTag = '[object String]' const symbolTag = '[object Symbol]' const weakMapTag = '[object WeakMap]' const arrayBufferTag = '[object ArrayBuffer]' const dataViewTag = '[object DataView]' const float32Tag = '[object Float32Array]' const float64Tag = '[object Float64Array]' const int8Tag = '[object Int8Array]' const int16Tag = '[object Int16Array]' const int32Tag = '[object Int32Array]' const uint8Tag = '[object Uint8Array]' const uint8ClampedTag = '[object Uint8ClampedArray]' const uint16Tag = '[object Uint16Array]' const uint32Tag = '[object Uint32Array]' /** Used to identify `toStringTag` values supported by `clone`. */ /** 用于标识“clone”支持的“toStringTag”值。 */ const cloneableTags = {} cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[float64Tag] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[mapTag] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[setTag] = cloneableTags[stringTag] = cloneableTags[symbolTag] = cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true cloneableTags[errorTag] = cloneableTags[weakMapTag] = false /** Used to check objects for own properties. */ /** 用于检查对象的自身属性。 */ const hasOwnProperty = Object.prototype.hasOwnProperty /** * Initializes an object clone based on its `toStringTag`. * 根据对象的“toStringTag”初始化对象克隆。 * **Note:** This function only supports cloning values with tags of * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`. * *注意:**此函数仅支持使用的标签克隆值 * “布尔”,“日期”,“错误”,“地图”,“数量”,“正则表达式”,“设置”,或“字符串”。 * @private * @param {Object} object 要克隆的对象。 * @param {string} tag 要克隆的对象的“toStringTag”。 * @param {boolean} [isDeep] 指定深度克隆。 * @returns {Object} 返回初始化的克隆。 */ function initCloneByTag(object, tag, isDeep) { const Ctor = object.constructor switch (tag) { case arrayBufferTag: return cloneArrayBuffer(object) case boolTag: case dateTag: return new Ctor(+object) case dataViewTag: return cloneDataView(object, isDeep) case float32Tag: case float64Tag: case int8Tag: case int16Tag: case int32Tag: case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: return cloneTypedArray(object, isDeep) case mapTag: return new Ctor case numberTag: case stringTag: return new Ctor(object) case regexpTag: return cloneRegExp(object) case setTag: return new Ctor case symbolTag: return cloneSymbol(object) } } /** * Initializes an array clone. * 初始化数组克隆。 * @private * @param {Array} array 要克隆的数组。 * @returns {Array} 返回初始化的克隆。 */ function initCloneArray(array) { const { length } = array const result = new array.constructor(length) // Add properties assigned by `RegExp#exec`. if (length && typeof array[0] === 'string' && hasOwnProperty.call(array, 'index')) { result.index = array.index result.input = array.input } return result } /** * The base implementation of `clone` and `cloneDeep` which tracks * traversed objects. * ' clone '和' cloneDeep '跟踪的基本实现 * 遍历对象。 * @private * @param {*} value 要克隆的值。 * @param {number} bitmask 位掩码的旗帜。 * 1 - Deep clone 深克隆 * 2 - Flatten inherited properties 继承属性 * 4 - Clone symbols 克隆符号 * @param {Function} [customizer] 函数自定义克隆。 * @param {string} [key] The key of `value`. * @param {Object} [object] The parent object of `value`. * @param {Object} [stack] 跟踪遍历的对象及其克隆副本。 * @returns {*} 返回克隆的值。 */ function baseClone(value, bitmask, customizer, key, object, stack) { let result const isDeep = bitmask & CLONE_DEEP_FLAG const isFlat = bitmask & CLONE_FLAT_FLAG const isFull = bitmask & CLONE_SYMBOLS_FLAG if (customizer) { result = object ? customizer(value, key, object, stack) : customizer(value) } if (result !== undefined) { return result } if (!isObject(value)) { return value } const isArr = Array.isArray(value) const tag = getTag(value) if (isArr) { result = initCloneArray(value) if (!isDeep) { return copyArray(value, result) } } else { const isFunc = typeof value === 'function' if (isBuffer(value)) { return cloneBuffer(value, isDeep) } if (tag == objectTag || tag == argsTag || (isFunc && !object)) { result = (isFlat || isFunc) ? {} : initCloneObject(value) if (!isDeep) { return isFlat ? copySymbolsIn(value, copyObject(value, keysIn(value), result)) : copySymbols(value, Object.assign(result, value)) } } else { if (isFunc || !cloneableTags[tag]) { return object ? value : {} } result = initCloneByTag(value, tag, isDeep) } } // Check for circular references and return its corresponding clone. // 检查循环引用并返回其对应的克隆。 stack || (stack = new Stack) const stacked = stack.get(value) if (stacked) { return stacked } stack.set(value, result) if (tag == mapTag) { value.forEach((subValue, key) => { result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)) }) return result } if (tag == setTag) { value.forEach((subValue) => { result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)) }) return result } if (isTypedArray(value)) { return result } const keysFunc = isFull ? (isFlat ? getAllKeysIn : getAllKeys) : (isFlat ? keysIn : keys) const props = isArr ? undefined : keysFunc(value) arrayEach(props || value, (subValue, key) => { if (props) { key = subValue subValue = value[key] } // Recursively populate clone (susceptible to call stack limits). // 递归地填充克隆(容易受到调用堆栈限制)。 assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)) }) return result } export default baseClone