Web/04-JavaScript基础/28-浅拷贝和深拷贝.md
2021-07-29 11:08:52 +08:00

217 lines
6.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 28-浅拷贝和深拷贝
publish: true
---
<ArticleTopAd></ArticleTopAd>
## 概念
- 浅拷贝只拷贝最外面一层的数据更深层次的对象只拷贝引用
- 深拷贝拷贝多层数据每一层级别的数据都会拷贝
**总结**
拷贝引用的时候是属于**传址**而非**传值**关于传值和传址的区别是很基础的内容详见JavaScript 基础/对象简介.md这篇文章
深拷贝会把对象里**所有的数据**重新复制到新的内存空间是最彻底的拷贝
## 浅拷贝的实现方式
### for in 实现浅拷贝比较繁琐
```js
const obj1 = {
name: 'qianguyihao',
age: 28,
info: {
desc: '很厉害',
},
};
const obj2 = {};
// 用 for in 将 obj1 的值拷贝给 obj2
for (let key in obj1) {
obj2[key] = obj1[key];
}
console.log('obj2:' + JSON.stringify(obj2));
obj1.info.desc = '永不止步'; // 当修改 obj1 的第二层数据时obj2的值也会被改变。所以 for in 是浅拷贝
console.log('obj2:' + JSON.stringify(obj2));
```
上方代码中 for in 做拷贝时只能做到浅拷贝也就是说 obj2 name age 这两个属性会单独存放在新的内存地址中 obj1 没有关系但是`obj2.info` 属性 `obj1.info`属性**它俩指向的是同一个堆内存地址**所以当我修改 `obj1.info` 里的值之后`obj2.info`的值也会被修改
打印结果如下
```
obj2:{"name":"qianguyihao","age":28,"info":{"desc":"很厉害"}}
obj2:{"name":"qianguyihao","age":28,"info":{"desc":"永不止步"}}
```
### Object.assgin() 实现浅拷贝推荐的方式
上面的 for in 方法做浅拷贝过于繁琐ES6 给我们提供了新的语法糖通过 `Object.assgin()` 可以实现**浅拷贝**
`Object.assgin()` 在日常开发中使用得相当频繁非掌握不可
**语法**
```js
// 语法1
obj2 = Object.assgin(obj2, obj1);
// 语法2
Object.assign(目标对象, 源对象1, 源对象2...);
```
**解释**`obj1` 拷贝给 `obj2`执行完毕后obj2 的值会被更新
**作用** obj1 的值追加到 obj2 如果对象里的属性名相同会被覆盖
从语法2中可以看出Object.assign() 可以将多个源对象拷贝到目标对象
** 1**
```js
const obj1 = {
name: 'qianguyihao',
age: 28,
info: {
desc: 'hello',
},
};
// 浅拷贝:把 obj1 拷贝给 obj2。如果 obj1 只有一层数据那么obj1 和 obj2 则互不影响
const obj2 = Object.assign({}, obj1);
console.log('obj2:' + JSON.stringify(obj2));
obj1.info.desc = '永不止步'; // 由于 Object.assign() 只是浅拷贝,所以当修改 obj1 的第二层数据时obj2 对应的值也会被改变。
console.log('obj2:' + JSON.stringify(obj2));
```
代码解释由于 Object.assign() 只是浅拷贝所以在当前这个案例中 obj2 中的 name 属性和 age 属性是单独存放在新的堆内存地址中的 obj1 没有关系但是`obj2.info` 属性 `obj1.info`属性**它俩指向的是同一个堆内存地址**所以当我修改 `obj1.info` 里的值之后`obj2.info`的值也会被修改
打印结果
```
obj2:{"name":"qianguyihao","age":28,"info":{"desc":"hello"}}
obj2:{"name":"qianguyihao","age":28,"info":{"desc":"永不止步"}}
```
** 2**
```js
const myObj = {
name: 'qianguyihao',
age: 28,
};
// 【写法1】浅拷贝把 myObj 拷贝给 obj1
const obj1 = {};
Object.assign(obj1, myObj);
// 【写法2】浅拷贝把 myObj 拷贝给 obj2
const obj2 = Object.assign({}, myObj);
// 【写法3】浅拷贝把 myObj 拷贝给 obj31。注意这里的 obj31 和 obj32 其实是等价的,他们指向了同一个内存地址
const obj31 = {};
const obj32 = Object.assign(obj31, myObj);
```
上面这三种写法是等价的所以当我们需要将对象 A 复制拷贝给对象 B不要直接使用 `B = A`而是要使用 Object.assign(B, A)
** 3**
```js
let obj1 = { name: 'qianguyihao', age: 26 };
let obj2 = { city: 'shenzhen', age: 28 };
let obj3 = {};
Object.assign(obj3, obj1, obj2); // 将 obj1、obj2的内容赋值给 obj3
console.log(obj3); // {name: "qianguyihao", age: 28, city: "shenzhen"}
```
上面的代码可以理解成将多个对象obj1和obj2合并成一个对象 obj3
**例4**重要
```js
const obj1 = {
name: 'qianguyihao',
age: 28,
desc: 'hello world',
};
const obj2 = {
name: '许嵩',
sex: '男',
};
// 浅拷贝:把 obj1 赋值给 obj2。这一行是关键代码。这行代码的返回值也是 obj2
Object.assign(obj2, obj1);
console.log(JSON.stringify(obj2));
```
打印结果
```
{"name":"qianguyihao","sex":"男","age":28,"desc":"hello world"}
```
注意** 4 在实际开发中会经常遇到一定要掌握**它的作用是 obj1 的值追加到 obj2 如果两个对象里的属性名相同 obj12 中的值会被 obj2 中的值覆盖
## 深拷贝的实现方式
深拷贝其实就是将浅拷贝进行递归
### for in 递归实现深拷贝
代码实现
```js
let obj1 = {
name: 'qianguyihao',
age: 28,
info: {
desc: 'hello',
},
color: ['red', 'blue', 'green'],
};
let obj2 = {};
deepCopy(obj2, obj1);
console.log(obj2);
obj1.info.desc = 'github';
console.log(obj2);
// 方法:深拷贝
function deepCopy(newObj, oldObj) {
for (let key in oldObj) {
// 获取属性值 oldObj[key]
let item = oldObj[key];
// 判断这个值是否是数组
if (item instanceof Array) {
newObj[key] = [];
deepCopy(newObj[key], item);
} else if (item instanceof Object) {
// 判断这个值是否是对象
newObj[key] = {};
deepCopy(newObj[key], item);
} else {
// 简单数据类型,直接赋值
newObj[key] = item;
}
}
}
```