0


前端 JS 浅拷贝与深拷贝

一、问题引出

基础类型的数据存放:

  1. let a = 100
  2. let b = a
  3. console.log("a:" + a, "b:" + b)
  4. a = 50
  5. console.log("a:" + a, "b:" + b)

打印:这里可以看到两个基础变量的值是互不影响的

这里a修改没有修改b的示意图如下:

基础类型的a和b都分别存放在栈内存里,当a被修改是b不会被影响。

下面我们将a和b换成对象类型:

  1. let a = [1, 2, 3]
  2. let b = a
  3. console.log("a:" + a, "b:" + b)
  4. a[0] = 100
  5. console.log("a:" + a, "b:" + b)

打印:可以看到,当a被修改是b也发生了改变。

此时的内存分配示意图如下:

由于对象是存在堆内存里的,当a赋值给b时,其实仅是b指向了a的同一个堆地址,当a修改此地址的对象时,b指向的对象就是该对象,所以会同步改变。

解决方案有两个,浅拷贝和深拷贝。

二、浅拷贝

1、通过解构重构实现浅拷贝
  1. let a = [1, 2, 3]
  2. let b = [...a]
  3. console.log("a:" + a, "b:" + b)
  4. a[0] = 100
  5. console.log("a:" + a, "b:" + b)

打印:此时,修改a并不会影响b的值。

此时的内存分配图如下:

通过以上方法,可以让b复制a的值重新生成一个新对象,在堆内存中重新开辟一个新的对象空间,此时,a和b的地址是不一样的。

你可能会说,这两个对象都完全独立开来了,怎么算浅拷贝呢,不应该是深拷贝吗?下面进一步实验:

  1. let a = [1, 2, 3, [4, 5]]
  2. let b = [...a]
  3. console.log(a)
  4. console.log(b)
  5. a[3][0] = 10
  6. console.log(a)
  7. console.log(b)

打印:可以看到当我实验二维数组时,二维数组里的内容被修改,还是会连同b一起修改.

此时的内存发布图如下:

浅拷贝只会拷贝最近一层的数据,二维数组为更进一层的数据,第一层存的只是第二层数据的地址,浅拷贝不会拷贝第二层以上的数据,这就是出现上述现象的原因。

如果此时a改的是第一项,不会堆b有任何影响,当a改的是共同指向的地址里的数据时,b的那部分数据也会随之改动。

三、深拷贝

对于二中提到的情况对于的深拷贝示意图如下:

下面我们将通过代码来实现深拷贝:

1、自定义实现深拷贝

实现深拷贝的函数:

  1. function deepClone(oldData) {
  2. if (typeof oldData === "object" && oldData !== null) {
  3. let res = Array.isArray(oldData) ? [] : {};
  4. for (let k in oldData) {
  5. if (oldData.hasOwnProperty(k)) {
  6. res[k] = deepClone(oldData[k])
  7. }
  8. }
  9. return res
  10. } else {
  11. return oldData
  12. }
  13. }

拷贝执行:

  1. let obj = {
  2. name: "Jack",
  3. age: 18,
  4. hobby: ["swimming", "jogging"],
  5. fn() {
  6. console.log(this.name)
  7. }
  8. }
  9. let obj2 = deepClone(obj)
  10. console.log(obj2)
  11. obj.hobby[0] = "play"
  12. obj.fn = function () {
  13. console.log("obj:" + this.age)
  14. }
  15. console.log(obj)
  16. console.log(obj2)
  17. obj.fn()
  18. obj2.fn()

打印:可以看到深拷贝成功了,函数也深拷贝成功了。

2、JSON实现深拷贝

网上有很多说可以实验JSON的序列化与反序列化实现深拷贝,但这样做其实是有缺点的,JSON的序列化与反序列化并不能拷贝函数类型

  1. let obj = {
  2. name: "Jack",
  3. age: 18,
  4. hobby: ["swimming", "jogging"],
  5. fn() {
  6. console.log(this.name)
  7. }
  8. }
  9. let obj3=JSON.parse(JSON.stringify(obj))
  10. console.log(obj3)

打印:可以看到序列拷贝的并没有函数对象

四、总结

1、如果是基本数据类型,js都是采用深拷贝的,数据之间不会有任何影响;

2、可以通过解构重构或Object.assgin方法来实现浅拷贝;

3、深拷贝可以使用实验中的深拷贝递归函数来实现,对于一些没有函数的对象可以实验JSON.parse(JSON.stringify()) 的方法来快速实现深拷贝。

总结到此!


本文转载自: https://blog.csdn.net/qq_50909707/article/details/143858508
版权归原作者 Dragon Wu 所有, 如有侵权,请联系我们删除。

“前端 JS 浅拷贝与深拷贝”的评论:

还没有评论