564 字
3 分钟
JavaScript实现深拷贝函数
实现深拷贝的常用方法
1. JSON.parse(JSON.stringify(obj)) [慎 用]
注 意
undefined、任意的函数、symbol的值,在序列化过程中都会被忽略Date日期调用了toJSON()将其转换成了stirng字符串,所以会被当做字符串处理NaN和Infinity格式的数值以及null都会被视为null- 其他类型的对象,包括
Map/Set/WeakMap/WeakSet,只会序列化可枚举的属性- 对于包含循环引用的对象(对象之间相互引用,形成无限循环),执行此方法,会抛出错误
let obj = { name: 'Ocean', say: function() { console.log('hello') }, birthday: new Date('1990/01/01')}let newObj = JSON.parse(JSON.stringify(obj))console.log(newObj) // { name:"Ocean",birthday: "1989-12-31T16:00:00.000Z"}2. structuredClone
IMPORTANT浏览器原生 API,支持循环引用和多种内置类型(如 Date、Map、Set、ArrayBuffer 等), 但是兼容性有限(Chrome 98+、Node.js 17+),不支持自定义类实例或 DOM 元素。
const cloned = structuredClone(original);3. 递归
TIP
- 使用weakMap(弱引用,不会阻止垃圾回收, 避免内存泄露)维护一个缓存,处理循环引用的情况
- 通过
Object.create(Object.getPrototypeOf(source))避免原型链断裂- 使用
Reflect.ownKeys遍历可包含Symbol类型
/** * 深拷贝 * @param {Object} source * @param {WeakMap} cache */export function deepClone(source, cache = new WeakMap()){ if (typeof source !== "object" || typeof source === null) { return source; }
if (cache.has(source)) { return cache.get(source); }
let result; // 处理特殊类型 const Constructor = source.constructor; switch (true) { case source instanceof Date: result = new Constructor(source) break case source instanceof RegExp: result = new Constructor(source.source, source.flags) break case source instanceof Map: result = new Constructor() source.forEach((val, key) => { result.set(deepClone(key, cache), deepClone(val, cache)) }) break case source instanceof Set: result = new Constructor() source.forEach((val) => { result.add(deepClone(val, cache)) }) break case Array.isArray(source): result = [] break default: // 保持原型链 result = Object.create(Object.getPrototypeOf(source)) }
// 写入缓存 cache.set(source, result)
// 遍历复制所有属性(包括Symbol类型) Reflect.ownKeys(source).forEach((key) => { if(source.hasOwnProperty(key)) { result[key] = deepClone(source[key], cache) } })
return result}总结
| 方法 | 优点 | 缺点 | 应用场景 |
|---|---|---|---|
| 递归实现 | 灵活可控,支持自定义类型 | 实现复杂,性能较差 | 无库依赖且需处理特殊类型 |
| structuredClone | 原生高效,支持内置类型 | 兼容性差,不支持自定义类 | 现代浏览器环境 |
| JSON 方法 | 简单兼容性好 | 丢失函数/特殊类型,不支持循环引用 | 简单数据对象拷贝 |
| Lodash | 功能全面,稳定可靠 | 需引入外部库 | 复杂对象且项目允许使用 Lodash |
JavaScript实现深拷贝函数
https://blog.oceanh.top/posts/frontend/js实现深拷贝的常用方法/