深拷贝和浅拷贝
深拷贝和浅拷贝针对的其实都是我们的引用类型,对于基本类型其实只有赋值操作。
浅拷贝适用:适用于单层
深拷贝适用:多层
实现方式
浅拷贝的实现方式:
- Object.assign
- 扩展运算符
- Array.prototype.slice()
- Array.prototype.concat()
- Array.from()
深拷贝的实现方式:
- JSON.parse(JSON.stringify(obj))
- 递归
- lodash的cloneDeep方法
浅拷贝
浅拷贝的实现方式:
- Object.assign
- 扩展运算符
- Array.prototype.slice()
- Array.prototype.concat()
- Array.from()
浅拷贝方法都只会复制对象和数组的第一层属性,对于嵌套对象或数组的修改会影响到原对象
浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,这时候就会影响到另一个对象。
简单举个例子: 我们现在想通过复制objA对象来创建一个objB对象,并且修改objB中的info属性,看看是否会影响objA对象。
const objA = {
name: 'zhangsan',
age: 18,
info: {
gender: 'male',
height: 180
}
}
console.log(objA, 'objA--更改以前');
const objB = objA;
objB.age = 20;
objB.info.gender = 'female';
console.log(objB, 'objB');
console.log(objA, 'objA--更改以后');
查看输出结果:
{
"name": "zhangsan",
"age": 20,
"info": {
"gender": "female",
"height": 180
}
},'objA--更改以前'
{
name: 'zhangsan',
age: 20,
info: { gender: 'female', height: 180 }
} 'objB'
{
name: 'zhangsan',
age: 20,
info: { gender: 'female', height: 180 }
} 'objA--更改以后'
查看输出我们可以发现,objB中的info属性被修改了,但是objA中的info属性也被修改了,这就是浅拷贝带来的问题。
基础类型中的age,浅拷贝成功了,但是引用类型中的info属性,浅拷贝并没有成功,因为浅拷贝只是拷贝了引用类型的地址,并没有拷贝地址指向的内容,所以修改objB中的info属性,objA中的info属性也被修改了。
1. Object.assign() 拷贝对象
const objA = {
name: 'zhangsan',
age: 18,
info: {
gender: 'male',
height: 180
}
}
const objB = Object.assign({}, objA);// 浅拷贝
objB.info.gender = 'female';
console.log(objB, 'objB');
console.log(objA, 'objA--更改以后');
2. 展开运算符(...)
浅拷贝对象
对象的浅拷贝可以使用展开运算符(...)来实现,例如:
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };
console.log(shallowCopy); // 输出: { a: 1, b: { c: 2 } }
shallowCopy.b.c = 3;
console.log(original.b.c); // 输出: 3 (原对象也受到影响)
浅拷贝数组
数组的浅拷贝可以使用展开运算符(...)来实现,例如:
const originalArray = [1, 2, [3, 4]];
const shallowCopyArray = [...originalArray];
console.log(shallowCopyArray); // 输出: [1, 2, [3, 4]]
shallowCopyArray[2][0] = 'changed';
console.log(originalArray); // 输出: [1, 2, ['changed', 4]]
3. Array.prototype.slice()
对于数组,可以使用 slice() 方法进行浅拷贝
const originalArray = [1, 2, [3, 4]];
const shallowCopyArray = originalArray.slice();
console.log(shallowCopyArray); // 输出: [1, 2, [3, 4]]
shallowCopyArray[2][0] = 'changed';
console.log(originalArray); // 输出: [1, 2, ['changed', 4]]
4. 使用 Array.from()
Array.from() 方法也可以用于创建数组的浅拷贝
const originalArray = [1, 2, [3, 4]];
const shallowCopyArray = Array.from(originalArray);
console.log(shallowCopyArray); // 输出: [1, 2, [3, 4]]
shallowCopyArray[2][0] = 'changed';
console.log(originalArray); // 输出: [1, 2, ['changed', 4]]
5. Array.prototype.concat() 拷贝数组
const arrA = [1, 2, 3, 4, 5];
const arrB = arrA.concat();// 浅拷贝
深拷贝
深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
- 深拷贝的实现方式:
- JSON.parse(JSON.stringify(obj))
- 递归
- lodash的cloneDeep方法
1、JSON.parse(JSON.stringify(obj))
JSON.parse(JSON.stringify(obj))是深拷贝最简单的一种方式,但是它有以下几个问题:
- 会忽略undefined、symbol和函数
- 不能序列化函数
- 不能解决循环引用的对象
- 不能正确处理new Date()和RegExp类型
- 不能处理原型链上的属性
使用
const objA = {
name: 'zhangsan',
age: 18,
info: {
gender: 'male',
height: 180
},
hobbyL:['篮球','足球']
}
const objB = JSON.parse(JSON.stringify(objA));
2、 递归
递归是深拷贝最常用的方法,但是需要注意循环引用的问题。
使用
- 深拷贝的递归方式实现思路如下:
简单手写一个深拷贝函数,然后判断一下类型,如果是对象或者数组,则递归调用深拷贝函数,否则直接返回该值,这里注意的就是先判断数组类型,因为数组也是对象,但是我们需要先判断数组,否则会报错。
// 深拷贝
const objA = {
name: 'zhangsan',
age: 18,
info: {
gender: 'male',
height: 180
},
hobbyL:['篮球','足球']
}
const o=new Object();
console.log(objA, 'objA--更改以前');
function deepClone(newObj, oldObj) {
for (let k in oldObj) {
let item = oldObj[k]; // 获取属性值
if (item instanceof Array) {
newObj[k] = []
deepClone(newObj[k], item)
} else if (item instanceof Object) {
newObj[k] = {}
deepClone(newObj[k], item)
} else {
newObj[k] = item
}
}
}
3、lodash的cloneDeep方法
lodash是一个js工具库,封装了很多常用的方法,其中就包括深拷贝方法,我们可以直接使用。
官方的在线地址: https://www.lodashjs.com/
安装:
浏览器环境:
<script src="lodash.js"></script>
通过 npm:
$ npm i -g npm
$ npm i --save lodash
使用
const _ = require('lodash');
const objA = {
name: 'zhangsan',
age: 18,
info: {
gender: 'male',
height: 180
},
hobbyL:['篮球','足球']
}
const objB = _.cloneDeep(objA);