
在JavaScript中实现深拷贝,尤其当数据结构中存在循环引用时,这可不是个小问题。说实话,刚接触深拷贝的时候,
JSON.parse(JSON.stringify(obj))
undefined
处理带有循环引用的JavaScript对象深拷贝,最可靠且现代的方案是利用浏览器或Node.js环境内置的结构化克隆算法。这个算法是HTML标准的一部分,它被用于多种场景,比如通过
postMessage
IndexedDB
structuredClone()
它的核心机制在于,在克隆过程中维护一个内部映射(通常是原对象到克隆对象的映射),当算法遍历到某个对象时,它会先检查这个对象是否已经被访问过并正在克隆中。如果发现已经存在于映射中,它就不会再次克隆,而是直接返回之前克隆好的对应实例,从而有效切断循环引用,避免栈溢出。
这个算法不仅能处理循环引用,还能正确克隆许多内置类型,比如
Date
RegExp
Map
Set
ArrayBuffer
TypedArray
Blob
File
ImageData
Symbol
structuredClone()
JSON.parse(JSON.stringify(obj))
其实,很多开发者,包括我自己在内,初期都非常依赖
JSON.parse(JSON.stringify(obj))
null
首先,它无法处理函数、undefined
Symbol
JSON.stringify
undefined
Symbol
其次,日期对象会被转换成ISO格式的字符串,而不是保持为一个
Date
RegExp
{}最关键的一点,也是标题中强调的,它无法处理循环引用。如果你的对象结构中存在 A 引用 B,B 又引用 A 的情况,
JSON.stringify
TypeError: Converting circular structure to JSON
structuredClone
尽管
structuredClone
以下是一个简化版的实现思路,核心是利用
WeakMap
function deepCloneManual(obj, map = new WeakMap()) {
// 1. 基本类型和 null 直接返回
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 2. 处理特殊内置对象,如 Date 和 RegExp
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof RegExp) {
return new RegExp(obj);
}
// 3. 检查循环引用:如果已克隆过,直接返回克隆后的对象
if (map.has(obj)) {
return map.get(obj);
}
// 4. 根据对象类型创建新的容器(数组或普通对象)
const clone = Array.isArray(obj) ? [] : {};
// 5. 存储映射:在递归克隆属性之前,将原对象和新克隆对象存入 WeakMap
// 这一步至关重要,它确保了在处理循环引用时能返回正确的克隆实例
map.set(obj, clone);
// 6. 递归克隆对象的属性或数组的元素
for (const key in obj) {
// 确保只处理对象自身的属性,而不是原型链上的属性
if (Object.prototype.hasOwnProperty.call(obj, key)) {
clone[key] = deepCloneManual(obj[key], map);
}
}
return clone;
}
// 示例用法:
// const obj = {};
// const a = { name: 'A' };
// const b = { name: 'B' };
// a.b = b;
// b.a = a; // 循环引用
// obj.a = a;
//
// const clonedObj = deepCloneManual(obj);
// console.log(clonedObj);
// console.log(clonedObj.a.b.a === clonedObj.a); // true,表示循环引用被正确处理这个
deepCloneManual
map.has(obj)
Map
Map
需要注意的是,这个手动实现比
structuredClone
Map
Set
ArrayBuffer
structuredClone
选择深拷贝方案时,我们总要权衡便利性、性能、兼容性和功能覆盖。
优先选择 structuredClone()
在绝大多数现代Web应用和Node.js环境中,
structuredClone()
Map
Set
ArrayBuffer
ImageData
考虑自定义实现或第三方库:
尽管
structuredClone()
lodash.clonedeep
Symbol
structuredClone()
structuredClone()
structuredClone()
writable
enumerable
总的来说,默认情况下,请使用 structuredClone()
structuredClone()
以上就是JS 深拷贝实现方案对比 - 处理循环引用的结构化克隆算法解析的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号