浅拷贝与深拷贝的关键在于明确隔离哪一层引用:浅拷贝适用于扁平结构或仅需顶层隔离的场景,深拷贝需据数据类型、循环引用等选方案,盲目套用易丢数据或栈溢出。

掌握浅拷贝与深拷贝的高级进阶技巧,关键不在记方法,而在理解“你到底想隔离哪一层引用”。很多开发者卡在“用了 Object.assign 还是被改了”,或“JSON.parse(JSON.stringify()) 突然丢数据”,问题根源不是语法写错,而是没判断清楚数据结构和使用场景。
看清数据结构:先问自己三个问题
动手前快速自检,比写十行代码更有效:
- 数组或对象里有没有嵌套的对象、数组?有 → 浅拷贝大概率不够用
- 有没有函数、Symbol、undefined、Date、RegExp、Map、Set?有 → JSON 方案直接失效
- 是否存在循环引用(比如 obj.parent = obj)?有 → 递归不加缓存会栈溢出
浅拷贝不是“不够好”,而是“刚好够用”的选择
别一上来就追求深拷贝。浅拷贝在大量场景下更合理、更快、更安全:
- React 中更新 state:setItems([...items, newItem]) —— 只需新数组引用,内部对象本就不该变
- 配置合并:Object.assign({}, defaultConfig, userConfig) —— 配置项通常是扁平 key-value
- 表单临时副本:const draft = { ...original }; 修改 name、email 这类字段完全没问题
记住:浅拷贝真正危险的,是你以为它“全断开了”,结果去改了 第二层及以后的属性。
深拷贝要分场景选方案,没有银弹
不同方案解决不同问题,硬套一个方法容易翻车:
- 简单纯数据 + 无特殊类型:JSON.parse(JSON.stringify(obj)) —— 快、短、易读,但明确知道它会吃掉函数、Symbol、undefined、NaN、Date 等
- 项目已引入 Lodash:_.cloneDeep(obj) —— 经过大量边界测试,支持循环引用、Map/Set/Date/RegExp 等,放心用
- 需要零依赖 + 完整控制:手写递归 + WeakMap 缓存 —— 核心逻辑三步:检测类型 → 遇到引用类型先查缓存 → 未见过则新建并存入缓存再递归
实战避坑:几个高频翻车点
这些细节不注意,100%引发线上问题:
- 用扩展运算符拷贝对象时,如果原对象有 getter/setter 或不可枚举属性,它们不会被复制
- Array.from() 和 slice() 对稀疏数组(如 new Array(5))行为不同:前者生成 [undefined, undefined, …],后者生成空槽位数组
- Object.assign() 不会触发 Proxy 的 set 拦截,也不复制原型链上的属性
- 自己写递归深拷贝时,一定要先判 null 和 typeof !== 'object',否则 null instanceof Object 是 false,但 null !== 'object' 为 true,容易漏处理











