概念
浅拷贝:分两种情况
- 如果属性是基本类型,会新开辟一个内存空间。
- 如果属性是引用类型,引用类型属性是还是指向同一块内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝:
- 将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
实现方式
浅拷贝
Object.assign()
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。
let obj1 = {
person:{
name:"xiaohe",
age:18
},
hobby:"run"
}
let obj2 = Object.assign({},obj1) // 浅拷贝
obj2.person.name = "xiaobai"; // 修改引用类型
obj2.hobby = "eat" // 修改基本类型
console.log(obj1)
console.log(obj2)
复制代码
输出结果:
代码链接:js.jirengu.com/hedemawajo/…
展开运算符
展开运算符是一个 es6 / es2015特性,它提供了一种非常方便的方式来执行浅拷贝,这与 Object.assign ()的功能相同。
let obj1 = {name:'xiaohe',address:{x:100,y:100}}
let obj2= {... obj1} // 展开运算符实现浅拷贝
obj2.address.x = 200; // 修改引用类型的值
obj2.name = 'xiaohong' // 修改基本类型的值
console.log(obj1)
console.log(obj2)
复制代码
输出结果:
代码链接:js.jirengu.com/yopihiduvu/…
Array.prototype.concat()
let arr = [1, 3, {username: 'xiaohe'}];
let arr2 = arr.concat(); // 浅拷贝
arr2[2].username = 'xiaobai';
arr2[1] = 2;
console.log(arr);
console.log(ar2);
复制代码
输出结果:
代码链接:js.jirengu.com/miraravayi/…
Array.prototype.slice()
let arr = [1, 3, {username: 'xiaohe'}];
let arr2 = arr.slice(); // 浅拷贝
arr2[2].username = 'xiaobai';
arr2[1] = 2;
console.log(arr);
console.log(arr2)
复制代码
输出结果:
代码链接:js.jirengu.com/werixenipi/…
小总结
当我们对一个对象进行浅拷贝时,在新对象对引用类型进行修改值,是会影响到原对象的引用类型的值(因为浅拷贝后的对象的引用类型是指向之前对象的内存地址,相对于共享一块内存地址的。而基本数据类型会开辟一个新的内存空间存放值,所以在我们对新对象修改基本类型的值时,是不会影响到原来对象的基本类型的值。
深拷贝
JSON.parse(JSON.stringify())
let arr = [1, 3, {username: ' xiaohe'}];
let arr2 = JSON.parse(JSON.stringify(arr));
arr2[1] = 2;
arr2[2].username = 'xiaoming';
console.log(arr, arr2)
复制代码
输出结果:
代码链接:js.jirengu.com/nimekidehu/…
解析:这也是使用 JSON.stringify 将JS对象转成JSON字符串,再用JSON.parse把字符串解析成JS对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。
注意点:这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify和JSON.parse处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。
示例代码:
let arr = [1, 3, {
username: 'xiaohe'
},function(){}];
let arr2 = JSON.parse(JSON.stringify(arr));
arr2[2].username = 'xiaobai';
console.log(arr, arr2)
复制代码
你可以在控制台或者编辑器使用 console.dir 打印 arr(深拷贝后) 和 arr2(深拷贝前) 看一下。(这里我直接放图片的对比)
===>
递归
递归的实现方式比较繁琐,后期如果遇到 JSON 解决不了深拷贝问题,再来了解
总结
浅拷贝
- 对一个对象进行浅拷贝,该对象下的基本类型属性会开辟新的空间,引用类型属性是还是指向同一块空间,所以当我们修改引用类型的值时,另一个对象也会被影响。
深拷贝
- 对一个对象进行深拷贝,会将原来的对象,完整的拷贝一份,开辟新的一块内存地址存放,当我们修改新对象的基本类型和引用类型,都不会影响到原来的对象。
- 使用 JSON 深拷贝存在一个问题,就是对象中由函数,深拷贝后得到的函数就不再是函数了,而是一个 null