注意事项
- 属性值是数组
- 循环应用
- 是否是自身的属性而不是原型链继承而来的
- 注意null这个特殊的值(类型是对象)
- 注意Date、RegExp、Function这些特殊的值
递归法
不考虑Date等特殊情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| function deepClone(obj) { function fn(obj,map = new Map()) { if (typeof obj !== 'object' || obj === null) { return obj } if (map.has(obj)) { return map.get(obj) } if (Array.isArray(obj)) { const newArray = []; map.set(obj, newArray); for (const item of obj) { newArray.push(fn(obj,map)) } return newArray } const newObj = {}; map.set(obj, newObj); for (const key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = fn(obj[key],map) } } return newObj } return fn(obj) }
const obj = { a: [1, 2, {e:'city'}], b: 13, c: undefined, d: { e: 'age' }, g: null } obj.d.f = obj.d const result = deepClone(obj) console.log(result.d === result.d.f)
|
完整的深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| function deepClone(obj) { function fn(obj,map = new Map()) { if (typeof obj !== 'object' || obj === null) { return obj } if (map.has(obj)) { return map.get(obj) } if (obj instanceof Date) { return new Date(obj) } if (obj instanceof RegExp) { return new RegExp(obj) } if (typeof obj === 'function') { return obj } if (Array.isArray(obj)) { const newArray = []; map.set(obj, newArray); for (const item of obj) { newArray.push(fn(obj,map)) } return newArray } const newObj = {}; map.set(obj, newObj); for (const key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = fn(obj[key],map) } } return newObj } return fn(obj) }
const obj = { b: 13, c: undefined, d: { e: 'age' }, g: null, h: new Date('2024-07-15'), j:/\d+/ }
const result = deepClone(obj) console.log(JSON.stringify(result),result.j instanceof RegExp)
|
迭代法
可以使用一个栈来存储用于拷贝的对象。走入while循环,退出条件是栈为空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| function deepClone(obj) { const stack = []; const map = new Map();
let newObj = Array.isArray(obj) ? [] : {}; map.set(obj, newObj); stack.push({ origin: obj, copy: newObj }) while (stack.length > 0) { const { origin, copy } = stack.pop();
for (let key in origin) { if (origin.hasOwnProperty(key)) { const value = origin[key]; if (value === null || typeof value !== 'object') { copy[key] = value; } else if (value instanceof Date) { copy[key] = new Date(value) } else if (value instanceof RegExp) { copy[key] = new RegExp(value) } else if (typeof value === 'function') { copy[key] = value } else if (map.has(value)) { copy[key] = map.get(value) } else { const newValue = Array.isArray(value) ? [] : {}; copy[key] = newValue; map.set(value, newValue); stack.push({origin:value,copy:newValue}) } } } } return newObj }
|