深浅拷贝
浅拷贝
- 浅拷贝: 把对象拷贝给一个新的对象,开发中我们经常需要复制一个对象,如果直接赋值,则复制的是地址,修改任何一个对象,另一个对象都会变化。
//直接赋值复制的是地址
const obj = { name: '佩奇' }
const newObj = obj
newObj.name = '乔治'
console.log(obj.name) // 也变成了乔治
浅拷贝常见方法:
- 拷贝对象:
-
Object. assign()
-
展开运算符:
{...obj}
- 拷贝数组:
Array.prototype.concat()
- 展开运算符
[...arr]
示例:
let p = {a: 1, b: {v: 2}};
let c = {...p};
p.a = 55;
p.b.v = 66;
console.log(p); // {a: 55, b: {v: 66}}
console.log(c); // {a: 1, b: {v: 66}}
- 浅拷贝只会复制对象的第一层属性,而不会递归地复制嵌套对象。
- 在上面的示例中,修改
p.a
的值只影响到p
,因为a
是一个顶层属性,而p.b.v
的修改影响到了c.b.v
,因为b
是一个嵌套对象,c.b
和p.b
引用同一个嵌套对象。
浅拷贝注意点:
- 如果是基本数据类型拷贝值
- 如果是引用数据类型拷贝的是地址,会相互影响。
- 拷贝原对象第一层基本数据类型的值和引用类型的地址。
- 第一层,如果属性值为引用类型 引用类型的值改变,会相互影响
深拷贝
- 在JavaScript中,深拷贝(deep copy)是指创建一个新的对象或数组,并且递归地复制原始对象或数组的所有嵌套属性和元素。深拷贝是在新的内存空间中创建一个完全独立的副本,与原始对象或数组完全无关。
- 在堆内存中新开辟一个空间,递归的拷贝原对象的所有属性和方法。
- 递归拷贝:一层一层,每一层对象都新创建一个内存空间
- 深拷贝:新旧对象,不会共享内存空间,互不影响。
实现深拷贝的方式:
1. JSON.parse(JSON.stringify(obj))
- 这个方法的原理是使用
JSON.stringify()
将原始对象或数组转换为JSON字符串,然后再使用JSON.parse()
将JSON字符串解析回JavaScript对象或数组。通过这个过程,我们可以获得原始对象或数组的一个完全独立的副本。 - JSON.stringify() 也叫做JSON序列化
- JSON.parse() 反序列化
const originalObject = { a: 1, b: 2, c: { d: 3 } };
const deepCopyObject = JSON.parse(JSON.stringify(originalObject));
// 修改原始对象中的属性值
originalObject.a = 10;
originalObject.c.d = 30;
console.log(originalObject); // { a: 10, b: 2, c: { d: 30 } }
console.log(deepCopyObject); // { a: 1, b: 2, c: { d: 3 } }
// 修改原始对象不会影响到深拷贝的对象,它们是完全独立的。
- 缺点:
- 无法复制函数、正则表达式等非原始数据类型,因为它们在序列化过程中会丢失。
- 对于循环引用的对象或数组,会导致无限递归的序列化,可能导致堆栈溢出的错误。
- 对象的原型链上的属性和方法也无法被复制
- 如果拷贝日期对象,会变成日期字符串
- 如果是NaN,Infinity, 属性值会变成null
2.lodash库
- 使用lodash库提供的
cloneDeep()
函数可以进行深拷贝。cloneDeep()
函数会递归地复制对象或数组及其嵌套的属性和元素,创建一个与原始对象完全独立的副本。
<!DOCTYPE html>
<html>
<head>
<title>Lodash Deep Copy Example</title>
</head>
<body>
<script src="path/to/lodash.js"></script>
<script>
const originalObject = { a: 1, b: 2, c: { d: 3 } };
const deepCopyObject = _.cloneDeep(originalObject);
// 修改原始对象中的属性值
originalObject.a = 10;
originalObject.c.d = 30;
console.log(originalObject); // { a: 10, b: 2, c: { d: 30 } }
console.log(deepCopyObject); // { a: 1, b: 2, c: { d: 3 } }
</script>
</body>
</html>
- 在上述示例中,我们在HTML文件中通过
<script>
标签引入了lodash库的源代码文件(lodash.js),然后使用_.cloneDeep()
函数对原始对象进行深拷贝。因此,修改原始对象不会影响到深拷贝的对象,它们是完全独立的。 - 确保在HTML文件中引入lodash库的位置和顺序正确,以确保在使用
_.cloneDeep()
函数之前已经成功加载了lodash库的源代码。
3.手写递归实现
function deepCopy(obj) {
if (obj === null || typeof obj !== "object") {
return obj;
}
let copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]);
}
}
return copy;
}
obj.hasOwnProperty(key)
这一步用于过滤对象的原型链上的属性,确保只复制对象自身的属性。因为for...in
循环会遍历对象的所有可枚举属性,包括从原型链继承的属性。
- 在上述代码中,
deepCopy()
函数接受一个参数obj
,表示需要进行深拷贝的对象或数组。函数首先检查obj
是否为null
或不是一个对象,如果是,则直接返回obj
。如果obj
是一个对象或数组,我们创建一个新的空对象或数组copy
,用于存储深拷贝后的副本。 - 然后,我们使用
for...in
循环遍历obj
的属性或数组元素,并使用递归调用deepCopy()
函数对每个属性或元素进行深拷贝。在每次递归调用中,如果属性或元素是一个对象或数组,我们将继续递归调用deepCopy()
函数,直到遍历完所有的嵌套属性和元素 - 最后,我们返回深拷贝后的
copy
对象或数组,它是一个与原始对象或数组完全独立的副本。
- 以下是一个使用
deepCopy()
函数进行深拷贝的示例:
const originalObject = { a: 1, b: 2, c: { d: 3 } };
const deepCopyObject = deepCopy(originalObject);
// 修改原始对象中的属性值
originalObject.a = 10;
originalObject.c.d = 30;
console.log(originalObject); // { a: 10, b: 2, c: { d: 30 } }
console.log(deepCopyObject); // { a: 1, b: 2, c: { d: 3 } }
转载自:https://juejin.cn/post/7236655739091042360