1.JSON转换
这是最简单的一种方式:
1
| JSON.parse(JSON.stringify(对象));
|
但这种方式有很大缺陷,由于它是依赖于JSON,因此它不支持JSON不支持的其他格式,通过JSON的官网可知,JSON只支持Object
、Array
、String
、Number
、Boolean
、Null
这几种数据类型,其他的比如Function
、Undefined
、Date
、RegExp
等数据类型都不支持。对于它不支持的数据都会直接忽略该属性。
另外,如果对象存在循环引用的情况,会导致栈溢出。
2.递归函数
通过JSON转换有那么多问题,那我们就需要自己手写一个深拷贝方法,最常用的就是通过递归实现。
(1) 基础版本
1 2 3 4 5 6 7 8 9 10 11
| function clone(target) { if (target instanceof Object) { let cloneTarget = {}; for (const key in target) { cloneTarget[key] = clone(target[key]); } return cloneTarget; } else { return target; } };
|
这种方式只能拷贝最简单的对象,还没有考虑其他引用类型:如数组、函数等。
(2) 拷贝数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function clone(target) { if (target instanceof Object) { let cloneTarget = null; if (target instanceof Array) { cloneTarget = []; } else { cloneTarget = {}; } for (const key in target) { cloneTarget[key] = clone(target[key]); } return cloneTarget; } else { return target; } }
|
(3) 拷贝函数
对于函数的拷贝其实是有争议的,我认为函数不应该有深拷贝,因为对于函数,绝大多数情况都是用来调用执行,很少用来操作函数对象,所以对于函数的拷贝,我认为只需要拷贝引用即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function clone(target) { if (target instanceof Object) { let cloneTarget = null; if (target instanceof Array) { cloneTarget = []; } else if (target instanceof Function) { cloneTarget = target; } else { cloneTarget = {}; } for (const key in target) { cloneTarget[key] = clone(target[key]); } return cloneTarget; } else { return target; } }
|
(4) 拷贝正则表达式
一个正则表达式由模式和修饰符组成,/test/
为正则模式,g
为修饰符。拷贝一个正则表达式,只需获取这两部分即可。通过正则对象的source
属性可以获取正则规则,flags
属性可以获取修饰符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function clone(target) { if (target instanceof Object) { let cloneTarget = null; if (target instanceof Array) { cloneTarget = []; } else if (target instanceof Function) { cloneTarget = target; } else if (target instanceof RegExp){ cloneTarget = new RegExp(target.source, target.flags); } else { cloneTarget = {}; } for (const key in target) { cloneTarget[key] = clone(target[key]); } return cloneTarget; } else { return target; } }
|
(5) 拷贝日期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function clone(target) { if (target instanceof Object) { let cloneTarget = null; if (target instanceof Array) { cloneTarget = []; } else if (target instanceof Function) { cloneTarget = target; } else if (target instanceof RegExp) { cloneTarget = new RegExp(target.source, target.flags); } else if (target instanceof Date) { cloneTarget = new Date(target); } else { cloneTarget = {}; } for (const key in target) { cloneTarget[key] = clone(target[key]); } return cloneTarget; } else { return target; } }
|
到目前为止,我们已经写出了一个可以使用的深拷贝函数,但是这个函数仍存在很多可以优化的地方。
3.进一步优化
(1) 忽略原型上的属性
我们在遍历对象属性的时候,使用的是for in
,for in
会遍历包括原型上的所有可迭代属性,但是事实上我们不应该这么做。所以我们需要通过hasOwnProperty
筛选出自身的属性。
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
| function clone(target) { if (target instanceof Object) { let cloneTarget = null; if (target instanceof Array) { cloneTarget = []; } else if (target instanceof Function) { cloneTarget = target; } else if (target instanceof RegExp) { cloneTarget = new RegExp(target.source, target.flags); } else if (target instanceof Date) { cloneTarget = new Date(target); } else { cloneTarget = {}; } for (const key in target) { if (target.hasOwnProperty(key)) { cloneTarget[key] = clone(target[key]); } } return cloneTarget; } else { return target; } }
|
(2) 循环引用问题
解决循环引用问题的关键点在于判断一个对象是否已经被拷贝过,如果拷贝过直接返回拷贝后的对象。所以我们需要一个东西帮我们记录,最好的方式就是map
。
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
| let cache = new Map(); function clone(target) { if (cache.get(target)) { return cache.get(target); } if (target instanceof Object) { let cloneTarget = null; if (target instanceof Array) { cloneTarget = []; } else if (target instanceof Function) { cloneTarget = target; } else if (target instanceof RegExp) { cloneTarget = new RegExp(target.source, target.flags); } else if (target instanceof Date) { cloneTarget = new Date(target); } else { cloneTarget = {}; } cache.set(target, cloneTarget); for (const key in target) { if (target.hasOwnProperty(key)) { cloneTarget[key] = clone(target[key]); } } return cloneTarget; } else { return target; } }
|
参考:
https://juejin.cn/post/6844903929705136141
https://juejin.cn/post/6889327058158092302