深拷贝(Deep Copy)和浅拷贝(Shallow Copy)都是复制对象的方法,但它们复制对象中的内容的深度不同。
-
浅拷贝(Shallow Copy):
- 浅拷贝创建了一个新对象,该新对象复制了原始对象的基本结构(属性和方法),但对于对象中的引用类型数据(例如对象、数组等),它只复制了引用,而不是实际的数据内容。(浅拷贝并不会复制这些引用类型数据的内容,而只是复制了对它们的引用(内存地址))这意味着新对象中的引用类型属性与原始对象中的引用类型属性指向相同的内存位置。它们共享相同的数据。
- 这意味着浅拷贝后的对象和原始对象共享相同的引用类型数据,如果修改浅拷贝后的对象中的引用类型数据,原始对象也会受到影响。
- 浅拷贝通常使用对象的展开运算符(
{...obj}
)、Object.assign()
或数组的.slice()
、.concat()
等方法来实现。
示例:
const original = { name: "John", pets: ["dog", "cat"] }; const shallowCopy = { ...original }; shallowCopy.pets.push("parrot"); console.log(original.pets); // ["dog", "cat", "parrot"],原始对象受到影响
-
深拷贝(Deep Copy):
- 深拷贝创建了一个新对象,该新对象不仅复制了原始对象的基本结构,还递归复制了原始对象中的所有引用类型数据,包括嵌套对象和数组,确保新对象与原始对象完全独立,不共享任何引用。
- 深拷贝通常需要
使用递归
或特定的深拷贝函数
来实现,因为它需要在复制过程中遍历整个对象的结构,确保每个引用类型数据都被复制。
示例:
const original = { name: "John", pets: ["dog", "cat"] }; const deepCopy = JSON.parse(JSON.stringify(original)); deepCopy.pets.push("parrot"); console.log(original.pets); // ["dog", "cat"],原始对象不受影响
需要注意的是,深拷贝可能会更耗费计算资源和时间,特别是对于大型、嵌套深度较大的对象。选择深拷贝或浅拷贝取决于你的需求,如果需要确保新对象与原始对象完全独立,不共享引用类型数据,那么应该使用深拷贝。否则,如果共享引用类型数据不是问题,浅拷贝可能更高效。
实现深拷贝的方式:
-
递归复制:
使用递归函数遍历原始对象的属性和嵌套引用类型数据,并创建一个新的对象来存储复制后的数据。这是手动实现深拷贝的一种方式,适用于各种对象结构。
缺陷:循环引用待解决function deepCopy(obj) { if (obj === null || typeof obj !== 'object') { return obj; // 如果是原始值或 null,则直接返回 } let copy = Array.isArray(obj) ? [] : {}; // 根据类型创建新对象或数组 for (let key in obj) { if (obj.hasOwnProperty(key)) { copy[key] = deepCopy(obj[key]); // 递归复制属性 } } return copy; }
-
JSON 序列化与反序列化:
使用JSON.stringify()
方法将原始对象转换为 JSON 字符串,然后再使用JSON.parse()
方法将 JSON 字符串转换为新的对象。这种方法对于能够被 JSON 序列化的对象非常有效
缺陷:1. 忽略函数 2. 忽略原型链3.忽略值是undefined的属性4. 当数据的层次很深会栈溢出function deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); }
-
第三方库:
有一些第三方 JavaScript 库(如 lodash 的_.cloneDeep()
方法)专门用于实现深拷贝,它们提供了高效且全面的深拷贝解决方案。const _ = require('lodash'); const deepCopy = _.cloneDeep;
-
StructuredClone:
structuredClone是结构化拷贝算法的实现,能够实现几乎对所有数据类型的深拷贝。目前最新版本的浏览器都已经原生支持。
structuredClone(value)//value:想拷贝的对象
structuredClone(value, { transfer })//transfer :可选的 可转移的对象:为一个数组,其中的值将被移动到新的对象,而不是拷贝到新的对象。
返回值:
返回传入对象深拷贝之后的对象,如果传入是不能进行深拷贝的对象,则抛出DATA_CLONE_ERR
错误
缺陷:
Function
和DOM
对象,如果对象中含有,将抛出DATA_CLONE_ERR
异常。- 不保留
RegExp
对象的lastIndex
字段。 - 不保留属性描述符,
setters
以及getters
(以及其他类似元数据的功能)。例如,如果一个对象用属性描述符标记为read-only
,它将会被复制为read-write
。 - 不保留原形链。
//copy普通对象
const original = {a:{b:1}}
const cloned = structuredClone(original)
console.log(cloned) // {a:{b:1}}
console.log(origin.a===cloned.a) // false
//拷贝函数
const original = {a:()=>{}}
const cloned = structuredClone(original)
// Uncaught DOMException: Failed to execute 'structuredClone' on 'Window': ()=>{} could not be cloned.