【JS基础】一文搞定深拷贝和浅拷贝

概念

浅拷贝:分两种情况

  • 如果属性是基本类型,会新开辟一个内存空间。
  • 如果属性是引用类型,引用类型属性是还是指向同一块内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象

深拷贝:

  • 将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

实现方式

浅拷贝

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 解决不了深拷贝问题,再来了解

总结

浅拷贝

  1. 对一个对象进行浅拷贝,该对象下的基本类型属性会开辟新的空间,引用类型属性是还是指向同一块空间,所以当我们修改引用类型的值时,另一个对象也会被影响。

深拷贝

  1. 对一个对象进行深拷贝,会将原来的对象,完整的拷贝一份,开辟新的一块内存地址存放,当我们修改新对象的基本类型和引用类型,都不会影响到原来的对象。
  2. 使用 JSON 深拷贝存在一个问题,就是对象中由函数,深拷贝后得到的函数就不再是函数了,而是一个 null

猜你喜欢

转载自juejin.im/post/7034864585689530405