程序员面试必问:JSON 深拷贝的 5 种实现方式
深拷贝是前端面试的高频题,也是工作中经常遇到的场景。这篇文章用 5 种方法实现 JSON 深拷贝,从最简单的到最完善的,帮你一次搞清楚。
什么是深拷贝?
在 JavaScript 中,对象是引用类型:
1 | const obj = { name: 'javahiker', skills: ['Java', 'React'] } |
上面是浅拷贝,只复制了引用。深拷贝要创建一个全新的对象,与原对象完全独立。
方法 1:JSON.parse(JSON.stringify()) — 最简单的方案
1 | function deepCopy(obj) { |
优点:
- 一行代码,极其简单
- 大部分场景够用
缺点: ⚠️ 很多坑
1 | const test = { |
结论: 面试中这个方法能说,但要立刻说出它的局限性。生产环境只适用于纯数据对象(没有函数、Date、RegExp、undefined 等)。
可以用在线 JSON 工具验证你的深拷贝结果:JSON 格式化工具
方法 2:递归实现 — 面试手写标准答案
1 | function deepCopy(obj, hash = new WeakMap()) { |
关键点:
- 用
WeakMap解决循环引用(防止obj.self = obj导致栈溢出) - 分别处理 Date、RegExp、Map、Set 等特殊类型
- 用
Object.keys+Object.getOwnPropertySymbols遍历所有属性
面试加分项: 主动提到用 WeakMap 而不只是 Map,因为 WeakMap 不会阻止垃圾回收。
方法 3:structuredClone() — 现代浏览器的原生方案
1 | // 一行代码,浏览器原生支持 |
支持的:
- ✅ 普通对象和数组
- ✅ 嵌套对象
- ✅ Date、RegExp、Map、Set
- ✅ ArrayBuffer、TypedArray
- ✅ Blob、File、ImageData
- ✅ 循环引用(自动处理)
不支持的:
- ❌ 函数
- ❌ Symbol
- ❌ WeakMap、WeakSet
- ❌ DOM 节点
- ❌ Error 对象
兼容性: Chrome 98+、Firefox 94+、Safari 15.4+(2022 年后的浏览器都支持)
1 | // 实际使用 |
建议: 现代项目中,没有函数属性的对象,直接用 structuredClone() 最省心。
方法 4:MessageChannel — 利用 postMessage 的序列化
1 | function deepCopy(obj) { |
原理: postMessage 在跨窗口通信时会对数据进行结构化克隆(和 structuredClone 使用相同的算法)。
优点:
- 浏览器原生,不需要 polyfill
- 比
JSON.parse/stringify支持更多类型
缺点:
- 异步的,使用起来不如
structuredClone方便 - 兼容性跟 structuredClone 类似
面试价值: 提到这个说明你了解浏览器的底层机制,但实际工作用 structuredClone 就够了。
方法 5:Lodash cloneDeep — 生产环境最稳妥
1 | import _ from 'lodash' |
特点:
- 处理了 99% 的边界情况
- 经过大量项目验证,坑最少
- 函数、Symbol 等
structuredClone不支持的类型也能处理
唯一缺点: 增加了打包体积。但如果你已经在用 lodash,这是最稳妥的选择。
1 | // lodash cloneDeep 支持的边界情况 |
5 种方法对比总结
| 方法 | 代码量 | 浏览器兼容 | 函数 | Date/RegExp | 循环引用 | 推荐场景 |
|---|---|---|---|---|---|---|
| JSON.parse/stringify | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ❌ | ❌ | ❌ | 纯数据对象 |
| 递归实现 | 多 | ⭐⭐⭐⭐⭐ | ❌ | ✅ | ✅ | 面试手写 |
| structuredClone | ⭐⭐⭐ | ⭐⭐⭐ | ❌ | ✅ | ✅ | 现代项目首选 |
| MessageChannel | ⭐⭐ | ⭐⭐⭐ | ❌ | ✅ | ✅ | 了解即可 |
| Lodash cloneDeep | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ✅ | ✅ | ✅ | 生产环境最稳 |
面试中怎么回答
面试官问”如何实现深拷贝”,建议的答题顺序:
- 先说最简单方案:
JSON.parse(JSON.stringify()),立刻说出它的局限性 - 再说原生方案:
structuredClone(),说明浏览器原生支持,2022 年后可用 - 手写递归版本: 重点展示
WeakMap处理循环引用,以及分类型处理 - 最后提 lodash: 生产环境推荐用现成的
如果面试官追问”能不能拷贝函数”——
回答:理论上可以,用 new Function('return ' + fn.toString())(),但作用域链会丢失,实践中很少这样用。函数不应该被拷贝,应该复用。
需要在线验证你的 JSON 数据结构?试试这个免费工具箱(纯前端,不上传服务器):
JSON 工具箱 — 格式化 / 校验 / 对比 / 代码生成