写一个深拷贝函数
-
使用 JSON.stringify 和 parse 这类只能适合数据结构简单的,不能循环引用的,没有function的
-
普通深拷贝
-
只考虑 Object Array
-
无法转换 Map Set 和 循环引用
function cloneDeep(obj: any) { if (typeof obj !== 'object' || !obj) return obj const result = obj instanceof Array ? [] : {} for (let key in obj) { if (obj.hasOwnProperty(key)) { result[key] = cloneDeep(obj[key]) // 递归调用 } } return result } // 不支持的场景 const a: any = { set: new Set([10, 20, 30]), map: new Map(['x', 10], ['y', 20]) } a.self = a // 循环引用 Uncaught RangeError: Maximum call stack size exceeded console.log(cloneDeep(a)) // 无法处理 Map Set 和 循环引用
-
-
上述,对set和map的数据结构不行,会把他们干掉
-
如果添加循环引用的数据,则直接报错
-
好的方案如下:
export function cloneDeep(obj: any, map = new WeakMap()): any { if (typeof obj !== 'object' || !obj) return obj // 避免循环引用 const objFromMap = map.get(obj) if (objFromMap) return objFromMap let target: any = {} map.set(obj, target) // Map if (obj instanceof Map) { target = new Map() obj.forEach((v, k) => { const v1 = cloneDeep(v, map) const k1 = cloneDeep(k, map) target.set(k1, v1) }) } // Set if (obj instanceof Set) { target = new Set() obj.forEach(v => { const v1 = cloneDeep(v, map) target.add(v1) }) } // Array if (obj instanceof Array) { target = obj.map(item => cloneDeep(item, map)) } // Object for (const key in obj) { const val = obj[key] target[key] = cloneDeep(val, map) } return target } const a: any = { set: new Set([10, 20, 30]), map: new Map([['x', 10], ['y', 20]]) } a.self = a // 循环引用 console.log(cloneDeep(a))
-
总结
- 功能完整性:考虑多种数据结构
- 鲁棒性,考虑循环引用
- 能否考虑到如上几点