十七、JavaScript深拷贝与浅拷贝
引言
在JavaScript中,对象的拷贝是一项常见的操作。浅拷贝和深拷贝是两种常用的拷贝方式。浅拷贝只复制对象的引用,而深拷贝创建了一个全新的对象,包含与原始对象相同的值和结构。深拷贝和浅拷贝各有适用的场景和注意事项。
本文将详细介绍如何实现一个完整而优雅的深拷贝函数,处理循环引用和特殊类型,优化性能,并探讨深拷贝和浅拷贝的应用场景、注意事项和相关属性。
1. 深拷贝的实现
实现一个完整而优雅的深拷贝函数需要考虑以下几个方面:
1) 基本类型和特殊类型的处理
在实现深拷贝函数时,首先需要处理基本类型(如字符串、数字、布尔值等)和特殊类型(如函数、正则表达式和日期对象等)。对于基本类型,直接返回其值即可。对于特殊类型,可以选择直接引用原始对象,而不进行复制。
function deepClone(obj) { // 处理基本类型 if (typeof obj !== 'object' || obj === null) { return obj; } // 处理特殊类型 if (obj instanceof RegExp) { return new RegExp(obj); } if (obj instanceof Date) { return new Date(obj.getTime()); } if (obj instanceof Function) { return obj; } // 处理普通对象和数组 const clone = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = deepClone(obj[key]); } } return clone;}
在上述代码中,我们使用 typeof 操作符判断基本类型,根据对象的类型选择适当的处理方式。对于函数、正则表达式和日期对象,我们使用相应的构造函数创建新的实例。
2) 处理循环引用
循环引用是指对象属性之间存在相互引用的情况,导致递归复制陷入无限循环。为了处理循环引用,我们可以使用一个额外的数据结构(如 Map 或 WeakMap)来存储已经复制的对象,以便在遇到循环引用时进行判断和处理。
下面是一个修改后的 deepClone 函数,解决了循环引用问题:
function deepClone(obj, map = new Map()) { if (typeof obj !== 'object' || obj === null) { return obj; } if (map.has(obj)) { return map.get(obj); } const clone = Array.isArray(obj) ? [] : {}; map.set(obj, clone); for (let key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = deepClone(obj[key], map); } } return clone;}
在上述代码中,我们使用 Map 数据结构来存储已经复制的对象。在每次递归调用时,我们首先检查 map 中是否存在当前对象的引用,如果存在则直接返回对应的副本。这样,我们可以避免陷入无限循环。
带你读《现代Javascript高级教程》十七、JavaScript深拷贝与浅拷贝(2)https://developer.aliyun.com/article/1349577?groupCode=tech_library