浅拷贝和深拷贝
本质上的原因是对象引用的是地址,直接赋值会吧引用地址也复制给新值。
浅复制只会将对象的各个属性进行依次复制,会把引用地址也复制。
深拷贝是会递归源数据,吧新值得引用地址给换掉。
lodash 的 cloneDeep
入口
<!-- 这两个 flog 是因为 baseClone 会被很多方法给调用,这两个适用于区分一些操作 -->
<!-- 1 是是否深度复制,4 是是否复制 Symbols 类型 -->
const CLONE_DEEP_FLAG = 1
const CLONE_SYMBOLS_FLAG = 4
function cloneDeep(value) {return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG)
}
核心逻辑
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)
// 获取 constructor
const tag = getTag(value)
if (isArr) {
// 初始化一个长度和源相等的数组
result = initCloneArray(value)
// 不是 deep 就直接复制了事
if (!isDeep) {return copyArray(value, result)
}
} else {
const isFunc = typeof value == 'function'
// Buffer.isBuffer, 对于 buffer 对象就直接复制了事
if (isBuffer(value)) {return cloneBuffer(value, isDeep)
}
if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
// 是否需要继承 proto
result = (isFlat || isFunc) ? {} : initCloneObject(value)
if (!isDeep) {
// 这里 deepclone 的 isFlat 是 0,走 copySymbols,这个方法主要是复制源上的 Symbols
return isFlat
? copySymbolsIn(value, copyObject(value, keysIn(value), result))
: copySymbols(value, Object.assign(result, value))
}
} else {
// 如果是 func 或 error,WeakMap 就直接返回了
if (isFunc || !cloneableTags[tag]) {return object ? value : {}
}
// 对 tag 位 true 的类型进行 clone
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)
// 对 map 递归 clone
if (tag == mapTag) {value.forEach((subValue, key) => {result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack))
})
return result
}
// 对 set 递归调用
if (tag == setTag) {value.forEach((subValue) => {result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack))
})
return result
}
// 是否是 TypedArray 类型
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
}
数组这里主要是会有个 exec 的特殊数组
function initCloneArray(array) {const { length} = array
const result = new array.constructor(length)
// Add properties assigned by `RegExp#exec`.
// RegExp.exec 返回的特殊数组
if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
result.index = array.index
result.input = array.input
}
return result
}
其他方法
Underscore 库或 JSON.parse 等