拷贝是啥?
在JS中,深拷贝和浅拷贝是针对对象的复制而言的。对象在JS中是引用类型,因此当我们将一个对象赋值给另一个变量时,实际上是将其引用复制了一份。因此,对其中一个变量所做的修改也会影响另一个变量。
浅拷贝
浅拷贝是啥?
浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用(地址),所以改变新对象,旧对象也会改变,因为新旧对象共享一块内存。
代码展示
let obj = {
name:"ls",
sex:"男",
arr:[1,2,3]
};
let obj1=obj;
obj1.arr[0]=10;
console.log(obj.arr[0]);//10
//Object.assign(目标对象, 源对象, ···)
let obj1 = {a:1};
let obj2 = {b:{d:2}};
let obj3 = {c:3};
Object.assign(obj3,obj1,obj2);
console.log(obj3);//{b: {d:2}};
console.log(obj3);//{a: 1,b: {d:2},c: 3}
obj3.b.d = 99;
console.log(obj3);//{a: 1,b: {d: 99},c: 3}
console.log(obj2);//{b: {d:99}};
从上面第一段代码可以看出当对象中有多层属性时, 正常的赋值就会产生浅拷贝现象,可能有的小伙伴可能对Object.assign()这个方法有点不熟悉,那我就在下面浅介绍一下这个方法
Object.assign(目标对象,源对象1,源对象2....);作用就是将一个或者多个源对象中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象。
食用说明:
如果目标对象与源对象具有相同的键(属性名),则目标对象中的属性将被源对象中的属性覆盖,后面的源对象的属性将类似地覆盖前面的源对象的同名属性。
Object.assign()
方法只会拷贝源对象可枚举的的自有属性到目标对象。该方法在源对象上使用 [[Get]]
,在目标对象上使用 [[Set]]
,因此它会调用 getter 和 setter。故它对属性进行赋值,而不仅仅是复制或定义新的属性。如果合并源对象包含 getter 的新属性到原型中,则可能不适合使用此方法。
如果要将属性定义(包括它们的可枚举性)复制到原型中,则应改用 Object.getOwnPropertyDescriptor() 和 Object.defineProperty() 方法。
如何实现:浅拷贝是指将一个对象的属性值复制到另一个对象中,但是只复制了属性值的引用,而没有复制它们的实际值。这意味着,如果原始对象的属性值是一个对象或数组,则两个对象将共享相同的子对象或子数组。浅拷贝可以使用Object.assign或展开操作符(...)来实现。
- 当对象只有一级属性是不存在浅拷贝;
- 当对象中有多级属性时,二级属性后就是浅拷贝;
深拷贝
深拷贝是啥?
复制并创建一个一模一样的对象,不共享内存,修改新对象,旧对象保持不变。
深拷贝是指将一个对象的所有属性以及其属性的属性(递归)复制到另一个对象中,不共享任何引用。这意味着,即使原始对象的属性值是一个对象或数组,两个对象也拥有不同的子对象或子数组。深拷贝可以使用递归和JSON.parse / JSON.stringify方法来实现。
代码展示
//使用递归去深拷贝
function deepClone(obj){
var objs = Array.isArray(obj)?[]:{};
if(obj&&typeof obj==="object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
if(obj[key]&&typeof obj[key]==="object"){
objs[key] = deepClone(obj[key]);
}else{
objs[key] = obj[key];
}
}
}
}
return objs;
}
//使用JSON对象去深拷贝
function deepClone(obj){
let _obj = JSON.stringify(obj);
let objClone = JSON.parse(_obj);
return objClone
}
let a=[0,1,[2,3],4],
b=deepClone(a);
方法分析
递归:
- 先使用Array.isArray()判断传入的值是数组还是对象
- 判断obj是不是对象
- 用for(in)将对象里面第一层的属性全部循环出来
- 用hasOwnProperty(key)检测传入对象里面的值是不是自有属性
- 判断obj里面的属性还是不是对象
- 如果是对象就利用递归将这个值重新传入函数里面直到所有子对象全部进入else
- 如果不是对象就直接将值加入到另一个对象里面
JSON对象
- 利用JSON.stringily(obj);将传入的对象先变为字符串格式,用一个变量存起来
- 利用JSON.parse(_obj)将""去掉再用一个变量存起来
- 这样就解决了两个变量指向一个地址的问题了,也就做到了深拷贝
- 注意:JSON对象这个方法对数组有用,但是对对象没用,原因就是JSON.parse(_obj)虽然可以将""去除,但是不能去除对象的
在测试的时候我又发现一个问题关于扩展符...的
在一维数组里面使用...就是深拷贝,多维数组就是浅拷贝
测试结果:
//一维数组
let a=[0,1,4];
let b=[...a];
let c=b;
b[2]=6;
console.log(a);//0,1,4
console.log(b);//0,1,6
console.log(c);//0,1,6
//多维数组
let a=[0,1,[2,3],4];
let b=[...a];
let c=b;
b[2][0]=6;
console.log(a);//0,1,[6,3],4
console.log(b);//0,1,[6,3],4
console.log(c);//0,1,[6,3],4