手写深拷贝

手写深拷贝

深拷贝

深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象(新旧对象不共享同一块内存),且修改新对象不会影响原对象(深拷贝采用了在堆内存中申请新的空间来存储数据,这样每个可以避免指针悬挂)

浅拷贝

如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址(新旧对象共享同一块内存),所以如果其中一个对象改变了这个地址,就会影响到另一个对象(只是拷贝了指针,使得两个指针指向同一个地址,这样在对象块结束,调用函数析构的时,会造成同一份资源析构2次,即delete同一块内存2次,造成程序崩溃);

深拷贝与浅拷贝的区别

  • 当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的
  • 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源

image-20230322123923546

深拷贝实现

先定义一个object对象

1
2
3
4
5
6
7
8
9
10
11
12
13
const obj1 = {
name:"张三",
age: 18,
address: {
city: "重庆"
},
hobby: ["编程", "篮球"],
fn: function() {
console.log(123);
}
}
const obj2 = obj1; // 此时进行的是浅拷贝,只拷贝obj1在栈中的内存地址
console.log(obj2)

image-20230322124948433

使用JSON.parse和JSON.stringify

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const obj1 = {
name:"张三",
age: 18,
address: {
city: "重庆"
},
hobby: ["编程", "篮球"],
fn: function() {
console.log(123);
}
}

const obj2 = deepClone(obj1);
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}

修改基本数据类型name的值,可以看到只有obj2发生变化

image-20230322125601926

修改引用类型 address集合,hobby数组的值,也是只有obj2发生变化

image-20230322125754897

image-20230322125824342

但是函数却没有拷贝过来

image-20230322130104913

JSON.parse() 方法用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象 因此undefined不能被转换并抛出异常:”undefined”不是有效的 JSON;

参考文章:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

image-20230322130450890

jQuery.extend()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
// 记得引入js的库
const obj1 = {
name:"张三",
age: 18,
address: {
city: "重庆"
},
hobby: ["编程", "篮球"],
fn: function() {
console.log(123);
}
}
const obj2 = $.extend(true,{},obj1);

image-20230322131723591

递归实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const obj1 = {
name:"张三",
age: 18,
address: {
city: "重庆",
phone: {
number: 111111,
a: {
b: 3,
e: [1,2,3]
}
}
},
hobby: ["编程", "篮球", {a: {b: {d:111}}}],
fn: function() {
console.log(123);
}
}

function deepClone(obj) {
if (typeof obj != "object" || obj == null) {
return obj;
}
let res = obj instanceof Array ? [] : {};
for(let key in obj) {
if (obj.hasOwnProperty(key)) {
res[key] = deepClone(obj[key]); // 这里如果不递归调用就相当于只深拷贝了一层,其他层就是浅拷贝
}
}
return res;
}

const obj2 = deepClone(obj1);

此方法没考虑循环引用问题

image-20230322215507281

循环引用问题

lodash工具包

https://www.lodashjs.com/

直接使用lodash提供给我们的函数_.cloneDeep

参考文章

https://juejin.cn/post/7134970746580762637

https://juejin.cn/post/7142434398225301534


手写深拷贝
http://example.com/2023/03/22/07.前端小课堂/07.手写深拷贝/
作者
Deng ErPu
发布于
2023年3月22日
许可协议