深拷贝代码的层层进化
用递归的方式手写实现深拷贝
我们先来看网上最常见的深拷贝实现代码
1 2 3 4 5 6 7 8 9 10 11 12
| function deepClone(obj) { let newObj = Array.isArray(obj) ? [] : {}; for (let key in obj) { let item = obj[key]; if (item instanceof Object) { newObj[key] = deepClone(item); } else { newObj[key] = item; } } return newObj; }
|
测试用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function Obj() { this.num = 1, this.str = 'abc', this.strObj = new String('def'), this.reg = /abc/, this.date = new Date(), this.arr = [1, 2, 3], this.child = { id: '1-1', child: { id: '1-1-1' } } return this; } Obj.prototype.p = '原型上的属性p'; let obj = new Obj(); let newObj = deepClone(obj);
console.log(obj); console.log(obj.__proto__.p);
console.log(newObj); console.log(newObj.__proto__.p);
|

可以发现它的特点有,
对于正则表达式、日期类型无法拷贝,
原始值包装类型,如new String('abc')
等不能正常拷贝,
非私有变量,obj
原型上的属性不会继承,而是克隆在了自身属性上。
针对以上情况,有如下改进版,
其中,高亮第4行、第7行解决了非私有变量的继承问题,
第13~16行对正则表达式和日期进行额外的判断处理。
{4,7,13-16}1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| function deepClone(obj) { if (obj === null) return null; let newObj = new obj.constructor; for (let key in obj) { if (obj.hasOwnProperty(key)) { let item = obj[key]; if (typeof item !== 'object') { newObj[key] = item; } else if (item instanceof RegExp) { newObj[key] = new RegExp(item); } else if (item instanceof Date) { newObj[key] = new Date(item); } else { newObj[key] = deepClone(item); } } } return newObj; }
|

但对于new String('abc')
等,还是不能正常拷贝,
最后,我尝试进行了一些修改,
{16,20}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 deepClone(obj) { if (obj === null) return null; let newObj = new obj.constructor; for (let key in obj) { if (obj.hasOwnProperty(key)) { let item = obj[key]; if (typeof item !== 'object') { newObj[key] = item; } else if (item instanceof Array) { newObj[key] = deepClone(item); } else if (Object.prototype.toString.call(item) === '[object Object]') { newObj[key] = deepClone(item); } else { newObj[key] = new item.constructor(item); } } } return newObj; }
|

以上三个版本,均没有实现对函数的深拷贝,只是引用,貌似也没有这个需求吧。。。
当然,以上的几种深拷贝方法没有绝对的对错,一切要看项目需要考虑到什么程度啦!
结构赋值
1 2 3 4
| function deepClone(obj) { return {...obj}; }
|
通过 JSON 对象实现深拷贝
通过将对象转换为json字符串,再转换为对象会重新开辟空间
1 2 3 4 5
| function deepClone2(obj) { var newObj = JSON.parse(JSON.stringify(obj)); return newObj; }
|
通过jQuery的extend方法实现深拷贝
1 2
| var array = [1,2,3,4]; var newArray = $.extend(true,[],array);
|
Object.assign()拷贝
1
| Object.assign(target, ...sources)
|
lodash函数库实现深拷贝
lodash很热门的函数库,提供了 lodash.cloneDeep()实现深拷贝