2026年了,对象复制后为什么修改第二层,原对象也跟着变?
对象复制后修改第二层导致原对象跟着变,根因是 Object.assign 和展开运算符只复制了第一层:内层对象仍指向同一个引用。2026年项目联调时遇到“改嵌套字段,原对象同步变化”的情况,基本都在这一机制内。要不要继续往下处理,取决于你要修改到第几层:只改第一层可以用浅拷贝;需要改第二层以上,就要换用 structuredClone 或不可变更新。
第一层和第二层,差别在哪里
Object.assign(target, ...sources) 会把源对象所有可枚举属性逐个复制到目标对象。遇到原始类型时传值,遇到对象或数组时只传引用。展开运算符 {...obj} 也遵循同一规则。
- 第一层字段是字符串、数字这类原始类型,新对象和原对象互不影响。
- 第二层及以上的属性,复制的是引用地址。例如 newObj.params.pageSize = 20,会直接改到 originalObj.params.pageSize。
- 数组也一样:arrayCopy[0].name = 'x' 会穿透到原数组。
所以判断是不是浅拷贝,不看写了几个等于号,而是看修改路径上是否存在共享的嵌套引用。只要内层对象没有被重建,改动就会越过边界。
先问三个问题,再决定要不要深拷贝
实际开发中不推荐先背结论,按如下顺序判断更稳。
- 我要修改到第几层?只改第一层,浅拷贝通常够;要改嵌套字段并保留原对象不变,就必须向深层复制。
- 原对象后续还会被使用吗?如果它被多个模块读取或作为全局配置,浅拷贝的深层修改会污染其他页面;如果只用于一次性计算,风险就小很多。
- 运行环境允许用原生 API 吗?Node.js 17+ 和较新浏览器支持 structuredClone;在不支持的环境中需要先检测,再决定用递归克隆还是第三方依赖。
顺序很重要:先确定修改深度,再考虑是否值得为隔离付出成本,最后看环境约束。多数“改坏原对象”的案例,走到第一问就能定位。
2026年深浅拷贝方案对比:成本与边界
需要深拷贝时,可先按下表核对各方案的成本和适用边界。这里的周期是维护与联调的经验区间,不是固定值。
- structuredClone:适合现代浏览器和 Node.js 17+。能处理 Date、Map、Set、RegExp,也会检查循环引用。接入成本约半天;若需要兼容旧 WebView,还要加能力检测和降级,常见区间为 1~2 天。
- JSON.parse(JSON.stringify(obj)):适合纯 JSON 配置。函数和 undefined 会被忽略,循环引用直接抛错。日常使用成本低,但若数据含 Date,返工区间约半天到一天。
- 递归克隆:可以自定义 Date、Function、原型,但边界较多。从实现到补测试,常见区间为 1~3 天;后续每增加一种数据类型,还可能叠维护时间。
- 不可变类库(如 Immer):适合状态更新频繁的全局 Store,通过结构共享减少复制成本。引入依赖并统一协作方式,学习与改造区间为 3~7 天。
从 2026 年前端交付的视角看,structuredClone 是多数项目的优先选,但要不要用它,仍取决于最低运行环境。项目若需要支持旧版安卓 WebView,递归克隆或 polyfill 是更稳妥的路径。
交付现场:一次筛选器配置的“串扰”返工
2026 年上半年,某管理后台项目出现过这类串扰。约束条件:三周交付周期,页面配置由后端一次性返回,项目没引入全局状态管理。做法:同事图方便,用 Object.assign 把接口配置复制到组件 data,再往嵌套的 options 字段上 push 新选项。结果 options 仍是原对象引用,改动污染了源配置,导致另外两个页面读取同一配置时选项重叠。
修这个坑用了两天。按团队复盘,类似浅拷贝误用造成的返工,在联调阶段的常见区间是半天判断错、1~3 天返工;嵌套层级越深、共享页面越多,代价越高。判断数据是否真的“隔离”,标准是:改完后不影响任何外部可见状态,才算拥有它。
适用与不适用边界
浅拷贝适合这些场景:表单初始化时只需要替换默认值的第一层;渲染只读快照且不会修改子结构;或者只想把对象换个变量名,不打算动原内存。浅拷贝成本低,引入深拷贝反而会让逻辑变重。
不适合浅拷贝的场景包括:数据超过两层,后续需要增删改嵌套节点;对象被多个模块共享,任何一方都不该改动公共数据;对象存在循环引用且需要序列化。这时继续用 Object.assign,累积的会是一笔技术债。
深拷贝也不是万能的。高频更新的大对象若整棵克隆,每帧会有几十毫秒到几百毫秒不等的开销(经验区间,随设备性能变化)。如果数据重点在“频繁小幅更新”,更适合用不可变类库的结构共享。
常见问题
改第一层的时候,为什么原对象没变?
因为第一层属性如果是字符串、数字等原始类型,复制时直接赋值,内存里是两份。只有属性值为对象或数组时,复制的才是引用。
展开运算符 {...obj} 算深拷贝吗?
不算。它和 Object.assign 一样只复制第一层,第二层还是引用。想通过展开运算符去隔离多层修改,通常达不到目的。
JSON.parse(JSON.stringify(obj)) 是不是就安全了?
不一定。它只保留 JSON 兼容数据,函数和 undefined 会丢,循环引用会抛错。纯配置数据能用,含 Date、Map 的业务数据需要谨慎。
structuredClone 在 2026 年还需要垫片吗?
现代浏览器与 Node.js 17+ 已内置,但部分旧 WebView 不支持。先检测 typeof structuredClone === 'function',不支持时再降级到递归克隆。
后端返回的配置对象能直接复制后修改吗?
先看你要改第几层。只替换第一级可以浅拷贝;要改嵌套的数组或对象,建议先解构出数据再扩展改动路径,或者使用深拷贝。
代码打开前先问一句:原对象还需要保持独立吗?对齐复制深度与修改意图,能省掉大部分联调返工。只改第一层,浅拷贝就够;要改第二层及以上,直接换 structuredClone 或不可变更新。先跑一次能力检测,再决定降级路径。
-
2026年了,需求评审都点头了,为什么联调时还在补空状态和加载态?
日期:2026年9月14日 阅读:74
-
CDN 刷新过了,为什么还有用户看到旧页面?
日期:2026年9月13日 阅读:74
-
Uni-app 同时发小程序和 App,平台差异越堆越多,2026 年从哪一步开始收?
日期:2026年9月12日 阅读:76
-
2026年了,Flutter 打出来的安装包偏大,是引擎的锅还是项目里塞多了东西?
日期:2026年9月11日 阅读:57
-
前端SEO(Google/百度)页面URL带#号,会影响收录吗?
日期:2026年9月10日 阅读:103




