Object.assign 方法虽然用起来很方便,但不少开发者在使用它合并嵌套对象时都遇到过问题——它本质上只做浅拷贝,并不会递归合并深层结构。简单来说,它只复制第一层属性,嵌套对象不会逐层合并,而是整个引用直接覆盖。如果不了解这一特性,在配置合并、状态更新等场景下很容易引发意外 bug。

换句话说,Object.assign 在对对象进行合并时仅处理顶层属性,不会递归合并深层嵌套。嵌套对象不会像期待的那样逐个字段叠加,而是整个引用被替换。如果对此没有清晰认知,在合并配置参数、更新应用状态等操作中极易埋下难以察觉的错误。
嵌套对象会被整体覆盖,而不是逐层合并
当多个源对象中包含同名的嵌套对象属性(例如 api 或 user.profile),后面出现的源对象会直接覆盖前面的,内部字段不会叠加保留。
- 举个具体例子:
{ api: { baseUrl: '/v1' } }和{ api: { timeout: 3000 } }合并后,得到的结果是{ api: { timeout: 3000 } },baseUrl会丢失。 - 这不是子属性的合并,而是直接将整个对象引用赋值——操作简单粗暴,后果往往出乎意料。
所有引用类型都只复制内存地址
数组、日期对象、正则表达式、普通对象……只要属性值是引用类型,Object.assign 就只复制其内存地址,而不会创建新副本。这意味着,任意一处修改都会影响所有持有该引用的对象。
- 例如:
const a = { list: [1, 2] }; const b = Object.assign({}, a); b.list.push(3);→ 结果a.list也会变成[1, 2, 3]。 - 更深层的嵌套如
user.address.city被修改时,同样会波及所有共享该引用的对象。
不会自动跳过 undefined 或 null 值
如果某个源对象的属性值为 undefined,它依然会被写入目标对象,将对应字段设为 undefined。null 的处理与之类似,但注意:null 作为整个源对象参数(而非属性值)时会被忽略。
Object.assign({x: 1}, {x: undefined})→ 结果是{x: undefined}Object.assign({x: 1}, null, {y: 2})→ 结果是{x: 1, y: 2}(null源被自动跳过)
要实现深度合并,必须另寻方案
Object.assign 本身并不提供深度合并功能。如果需要嵌套对象的递归合并,可以参考以下做法:
- 使用
structuredClone()(现代浏览器环境)先深度克隆对象,再执行合并 - 借助
lodash.merge(),该方法会递归合并嵌套结构 - 自行编写递归函数,灵活控制合并逻辑(例如跳过
undefined、保留默认值等)
