实现一个深拷贝
1. 简单版
let obj = { 'a': 'a', num: 123, sing() { console.log('我会唱歌'); } } let copyObj = JSON.parse(JSON.stringify(obj)); console.log(copyObj); // {a: 'a', num: 123}这种方法可以拷贝一个只有基本数据类型的对象,但对于存在引用类型、函数和循环引用等是不行的。
2. 基础版(无法循环引用)
let clone = function (obj) { if (typeof obj === 'object') { let copyObj = Array.isArray(obj) ? [] : {}; for (const key in obj) { copyObj[key] = clone(obj[key]) } return copyObj; } else { return obj; } } let obj = { 'a': 'a', num: 123, o: { name: '刘德华' }, arr: [1, 2, 3], sing() { console.log('我会唱歌'); } } let copyObj = clone(obj); obj.o.name = '张学友'; obj.arr[0] = 0; console.log(copyObj);这种方法可以实现深拷贝,但对于存在循环引用的对象是无法做到拷贝的,因为会内存溢出。
3. 基础改良版(解决循环引用问题)
let clone = function (obj, map = new Map()) { if (typeof obj === 'object') { let copyObj = Array.isArray(obj) ? [] : {}; if (map.get(obj)) { return map.get(obj); } map.set(obj, copyObj); for (const key in obj) { copyObj[key] = clone(obj[key], map) } return copyObj; } else { return obj; } } let obj = { 'a': 'a', num: 123, o: { name: '刘德华' }, arr: [1, 2, 3], sing() { console.log('我会唱歌'); }, } obj.self = obj; let copyObj = clone(obj); console.log(copyObj);这种是上面基础改良版,解决了循环引用问题,做法是:我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题。
4.优化版
let clone = function (obj, map = new WeakMap()) { // 这里使用 WeakMap 弱引用数据类型 if (typeof obj === 'object') { let copyObj = Array.isArray(obj) ? [] : {}; if (map.get(obj)) { return map.get(obj); } map.set(obj, copyObj); for (const key in obj) { copyObj[key] = clone(obj[key], map) } return copyObj; } else { return obj; } } let obj = { 'a': 'a', num: 123, o: { name: '刘德华' }, arr: [1, 2, 3], sing() { console.log('我会唱歌'); }, } obj.self = obj; let copyObj = clone(obj); console.log(copyObj);在这种方法里,我们使用
WeakMap弱引用数据类型来替换Map类型,可以起到利用垃圾回收机制自动帮我们回收对象和拷贝对象的对应关系所使用的内存空间。此外,可以考虑使用
while循环替代for...in循环,以提高执行效率。
最后,可能还会碰到拷贝其它的数据类型,可以抽取一个判断数据类型的函数,再加以修改上面的版本即可。
posted on
浙公网安备 33010602011771号