引言
在前端开发中,经常会遇到需要复制对象或数组的情况。这时候就会用到深拷贝和浅拷贝的概念。深拷贝和浅拷贝是前端面试中的高频考点,理解它们的区别和应用场景对于面试以及日常开发都非常重要。本文将详细介绍深拷贝和浅拷贝的概念、区别以及常见的实现方式。
正文
通常我们在聊到深拷贝的浅拷贝的时候,聊得一般都是引用类型的值,比如对象和数组,而原始类型的值我们一般是不聊的,因为原始类型的值无一例外都是深拷贝。
浅拷贝
大家都知道,引用类型的值都是存在堆中而不是栈中。浅拷贝是指在复制对象或数组时,只复制一层对象的引用而不是实际的对象本身。也就是说,新对象与原对象共享同一个内存地址,其中一个对象的修改会影响到另一个对象。
js
let a = {
age:18
}
let b = a
// b的age会随着a的age的改变而改变,这就叫浅拷贝
a.age=20
console.log(b.age);//20
实际上常见的拷贝方法几乎都是浅拷贝,对象身上的赋值运算,Object.assign(),Object.created(),数组的解构,concat,slice以及arr.toReversed().reverse()等都是浅拷贝。那么,就不存在深拷贝的方法了吗?显然是存在的
深拷贝
什么是深拷贝? 深拷贝是指在复制对象或数组时,不仅复制对象本身,还要递归地复制对象的所有子对象,使得新对象与原对象完全独立,互不影响。目前的话就原生js中,引用类型的深拷贝就只有通过将对象转为字符串,再把字符串转为对象这一方法被广泛使用。
let obj = { name: 'Alice', age: 20 };
let newObj = JSON.parse(JSON.stringify(obj));
通常情况下,这种方法能够很好地对引用类型的值进行深拷贝,但遗憾的是,使用 JSON.stringify() 和 JSON.parse() 进行深拷贝时,会忽略 undefined、Symbol 和函数等特殊值。导致无法拷贝,并且,若尝试拷贝bigInt类型的值或者是循环引用的数据,会直接报错。
js
let obj = {
name: '我真的很困',
like: {
type: 'coding',
a: undefined,
b: null,
c: function () {
console.log('hello');
},
e: Symbol('hello'),
// acef都拷贝不了
}
}
let newObj= JSON.parse(JSON.stringify(obj))
console.log(newObj);
js
//尝试深拷贝bigInt类型
let obj={
a:123n
}
let newObj= JSON.parse(JSON.stringify(obj))
console.log(newObj);
说到这里就不得不提一下lodash这个库了,lodash提供了cloneDeep()方法,允许我们对引用类型的值进行深拷贝,并且不会出现上面的情况。
js
const dc = require('lodash');
let obj = { name: 'Alice', age: 20 };
let newObj = dc.cloneDeep(obj);
不过据说还有另一种方法 ,但可惜目前好像除了谷歌浏览器以外没有支持的浏览器的,属于是剑走偏锋出奇效的了。本文不做讲解,感兴趣的各位可以自行了解
深拷贝和浅拷贝的应用场景
浅拷贝适用于只需要复制一层对象引用的情况,比如克隆一个对象用于修改而不影响原始对象。
深拷贝适用于需要完全独立的对象副本的情况,比如避免修改原始对象或者将对象传递给其他函数时产生副作用。
需要根据具体的业务需求选择合适的拷贝方式,避免出现意料之外的问题。
总结
深拷贝和浅拷贝是前端开发中常见的概念,对于理解和应用它们的区别非常重要。浅拷贝只复制对象的一层引用,而深拷贝递归地复制对象及其子对象。在实际开发中,我们要根据具体的需求选择合适的拷贝方式,以确保代码的正确性和效率。