防坑关键在于验证“改嵌套属性是否影响原对象”。栈存基本类型(真复制),堆存引用类型(只存地址);浅拷贝仅隔离第一层,深拷贝需按需选型:JSON方案快但脆弱,structuredClone支持广但不兼容旧Safari,lodash.cloneDeep最稳妥。

防坑的关键不是记方法,而是盯住“改嵌套属性会不会影响原对象”这一句验证逻辑。只要每次拷贝后动手改一层嵌套值、立刻检查原对象是否同步变化,绝大多数坑都能当场暴露。
先搞清坑从哪来:栈和堆的分工必须刻进DNA
基本类型(string/number/boolean/null/undefined/Symbol/BigInt)存在栈里,赋值就是真复制一份;引用类型(Object/Array/Date/RegExp/Map/Set/Function)存在堆里,变量只存一个地址。你写的 const b = a,对对象来说根本不是拷贝,是给同一块堆内存配了第二把钥匙。
- 看到 obj2.name = '李四' 导致 obj1.name 也变,说明没做任何拷贝,只是引用传递
- 看到 copy.features.darkMode = false 同时改了原始配置,说明用了浅拷贝
- 看到 JSON.parse(JSON.stringify(obj)) 把 Date 变成字符串、function 消失、undefined 被跳过,说明掉进了序列化陷阱
浅拷贝工具怎么用才不翻车
Object.assign()、展开运算符 {...obj}、slice()、concat()、Array.from() 都只保第一层独立。它们不是错,是能力边界清晰——别指望它们处理嵌套。
- 适合场景:扁平对象/数组、仅需隔离顶层字段(比如合并默认配置和用户配置)
- 典型翻车:用 {...config} 复制含 filters: { price: { min: 0 } } 的对象,然后改 copy.filters.price.min,结果默认配置被污染
- 安全做法:只在确认所有值都是基本类型,或明确接受嵌套共享的前提下使用
深拷贝方案选型要按需,别迷信“最全”
没有银弹。不同方案覆盖范围、性能、兼容性差异很大,强行统一用一种反而更容易出事。
- JSON.parse(JSON.stringify()):快但脆弱。只用于简单数据结构(纯对象/数组,不含函数、Date、undefined、Symbol、循环引用)
- structuredClone():现代浏览器原生支持,能处理 Map/Set/Date/RegExp/ArrayBuffer,但不支持函数和循环引用,且 Safari 支持较晚(iOS 16.4+)
- lodash.cloneDeep():生产环境首选,覆盖全面,有循环引用防护,API 稳定。体积略大,但换来的是省心
- 手写递归:适合教学或极简场景。必须加 WeakMap 缓存已拷贝对象,否则循环引用直接爆栈
实战中三个必验动作
写完拷贝代码,别急着提交,三行验证比读十篇文档管用:
- 改嵌套属性:copy.nested.value = 'test'; console.log(original.nested.value) —— 输出 'test' 就是浅了
- 查特殊值:console.log(typeof copy.date, typeof copy.fn) —— Date 变字符串、fn 是 undefined,说明 JSON 方案失效
- 测循环引用:const obj = {}; obj.self = obj; const copy = yourClone(obj); console.log(copy.self === copy) —— 应该为 true,否则会报错或卡死


















