什么是深拷贝什么是浅拷贝

深拷贝

修改新变量的值不会影响原有变量的值,默认情况下 ​​基本数据类型​​ 都是深拷贝,示例代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script>
let num1 = 123;
let num2 = num1;

// 修改新变量的值
num2 = 666;
console.log(num1);
console.log(num2);
</script>
</head>
<body>
</body>
</html>

内存结构如下图:

JavaScript-深拷贝和浅拷贝_内存结构

浅拷贝

修改新变量的值会影响原有的变量的值,默认情况下引用类型都是浅拷贝,示例代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script>
class Person {
name = "BNTang";
age = 34;
}

let p1 = new Person();
let p2 = p1;

// 修改变量的值
p2.name = "zs";
console.log(p1.name);
console.log(p2.name);
</script>
</head>
<body>
</body>
</html>

内存结构如下图:

JavaScript-深拷贝和浅拷贝_内存结构_02

对象深拷贝

经过如上的代码我们已经知道了对象的拷贝是浅拷贝,如果想要实现对象的深拷贝的话,你就要知道如上图的原理就是因为它们之间操作的都是同一块存储空间导致的,如果想要实现深拷贝,是不是就只需要将它们的存储空间划分开来即可,内存存储结构图与实现代码如下:

JavaScript-深拷贝和浅拷贝_html_03

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script>
class Person {
name = "BNTang";
age = 34;
}

let p1 = new Person();
let p2 = new Object();
p2.name = p1.name;
p2.age = p1.age;
p2.name = "zs";

console.log(p1.name);
console.log(p2.name);
</script>
</head>
<body>
</body>
</html>

如上的代码实现起来有点 low 了,可以利用之前讲解的高级 for 遍历对象的方式进行复制对象,改造之后代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script>
class Person {
name = "BNTang";
age = 34;
}

let p1 = new Person();
let p2 = new Object();
for (let key in p1) {
p2[key] = p1[key];
}
p2.name = "zs";

console.log(p1.name);
console.log(p2.name);
</script>
</head>
<body>
</body>
</html>

通过我的观察我个人觉得如上的代码还是有点多了,其实在 JS 当中已经提供了对象的复制这种方法,我们来看看吧就是利用 ​​Object.assign​​ 方法可以将第二个参数的对象的属性和方法拷贝到第一个参数的对象中,注意点:只有被拷贝对象中所有属性都是基本数据类型,以下代码才是深拷贝:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script>
class Person {
name = "BNTang";
age = 34;
}

let p1 = new Person();
let p2 = new Object();
Object.assign(p2, p1);
p2.name = "zs";
console.log(p1.name);
console.log(p2.name);
</script>
</head>
<body>
</body>
</html>

其实如上的对象深拷贝还是存在问题的,如果拷贝的对象当中的一个属性又是一个对象呢,这个时候又会造成一个问题,就是拷贝的对象当中的那个属性如果是对象类型的话就会造成,两个对象引用的是同一块存储空间,如下图所示:

JavaScript-深拷贝和浅拷贝_html_04

如下代码就是会产生如上图中所说引用的都是同一块存储空间的问题:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script>
class Person {
name = "BNTang";
cat = {
age: 3
};
}

let p1 = new Person();
let p2 = new Object();

p2.cat = p1.cat;
p2.cat.age = 666;
console.log(p1.cat.age);
console.log(p2.cat.age);
</script>
</head>
<body>
</body>
</html>

如上代码内存结构图如下:

JavaScript-深拷贝和浅拷贝_内存结构_05

如上代码就是深拷贝最终的代码,以后你如果有对对象进行深拷贝的需求你直接拿过去用即可代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<script>
class Person {
name = "BNTang";
cat = {
age: 3
};
scores = [1, 3, 5];
}

let p1 = new Person();
let p2 = new Object();

depCopy(p2, p1);

p2.cat.age = 666;
console.log(p1.cat.age);
console.log(p2.cat.age);

console.log(p1);
console.log(p2);

function depCopy(target, source) {
// 1.通过遍历拿到source中所有的属性
for (let key in source) {
// console.log(key);

// 2.取出当前遍历到的属性对应的取值
let sourceValue = source[key];
// console.log(sourceValue);

// 3.判断当前的取值是否是引用数据类型
if (sourceValue instanceof Object) {
// console.log(sourceValue.constructor);
// console.log(new sourceValue.constructor);
let subTarget = new sourceValue.constructor;
target[key] = subTarget;
depCopy(subTarget, sourceValue);
} else {
target[key] = sourceValue;
}
}
}
</script>
</head>
<body>
</body>
</html>

最终内存结构图如下:

JavaScript-深拷贝和浅拷贝_深拷贝_06

JavaScript-深拷贝和浅拷贝_html_07