如何区分深拷贝和浅拷贝
1 2 3 4
| 假设B复制了A; 当修改了A时,看B是否会发生变化; 如果B也跟着变了,说明这是浅拷贝; 如果B没变,那就是深拷贝。
|
基本数据与复杂(引用)数据
1 2
| 基本数据类型:number,string,boolean,null,undefined,symbol(ES6),BigInt(ES10); 引用数据类型:Object,Array,function
|
1.基本数据类型名值存储在栈内存中
例如: let a = 1;
当你b=a复制时,栈内存会新开辟一个内存
所以当你此时修改a=2时,对b并不会对a造成影响,所以深拷贝本身只针对较为复杂的引用数据类型。
2.引用数据类型名存在栈内存中,而值却存在于堆内存中,栈内存会提供一个引用的地址指向堆内存中的值
例如: let a = [0,1,2,3,4]
当b=a进行拷贝时,其实复制的是a的引用地址,而并非是堆内存里面的值
而当我们修改a[0]=1时,由于a与b指向的是同一个地址,所以自然b也受到了影响,这就是浅拷贝了。
那么,我们如何在堆内存中也开辟一个新的内存专门为b存放值,就像基本类型一样,就能达到深拷贝了。
如何实现浅拷贝
- for…in只循环第一层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function simpleCopy(obj1) { var obj2 = Array.isArray(obj1)?[]:{}; for (let i in obj1) { obj2[i] = obj1[i] } return obj2; }
var obj1 = {a: 1, b: 2, c: { d: 3}}; var obj2 = simpleCopy(obj1); obj2.a = 3; obj2.c.d = 4;
|
- Object.assign
1 2 3 4 5
| var obj = {a: 1, b: 2} var obj1 = Object.assign({}, obj);
obj1.a = 3;
|
- 直接用=赋值
1 2 3 4 5 6
| let a = [0,1,2,3,4]; let b = a;
a[0] = 1;
|
如何实现深拷贝
- 采用递归去拷贝所以层级属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function deepClone (obj) { let objClone = Array.isArray(obj) ? [] : {}; if(obj && typeof obj === 'object') { for (let key in obj) { if(obj.hasOwnProperty(key)) { if(obj[key] && type obj[key] === 'object') { objClone[key] = deepClone(obj[key]); } else { objClone[key] = obj[key]; } } } } return objClone; }
let a = [1,2,3,4]; let b = deepClone(a); a[0] = 2;
|
- 通过JSON对象来实现深拷贝
1 2 3
| function deepClone2 (obj) { return JSON.parse(JSON.stringify(obj)); }
|
缺点:无法实现对象中方法的深拷贝,会显示为undefined;
- 通过Jquery的extend方法实现深拷贝
1 2
| let arr = [1,2,3,4]; let newArr = $.extend(true, [], arr);
|
- 通过lodash函数实现深拷贝
1
| let result = _.cloneDeep(test);
|
- Reflect法
1 2 3 4 5 6 7 8 9 10 11 12
| function deepClone (obj) { if (!isObject(obj)) { throw new Error('obj 不是一个对象') } let isArray = Array.isArray(obj); let cloneObj = isArray ? [...obj] : { ...obj }; Reflect.ownKeys(cloneObj).forEach(key => { cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]; }) return cloneObj; }
|
参考文案
- js浅拷贝与深拷贝的区别和实现方式