object.assign 执行浅合并,从左到右覆盖同名属性,不深拷贝、不跳过 undefined/null、忽略不可枚举属性,嵌套对象整块替换;应始终用空对象作 target 防止污染源,并慎用于深层配置。

Object.assign 会按参数顺序覆盖同名属性
它从左到右依次遍历源对象,遇到相同 key 时,后一个源对象的值直接覆盖前一个——不是“深度合并”,也不是“跳过已存在属性”。target 对象本身会被修改,且返回值就是该 target。
常见错误现象:Object.assign({}, config1, config2) 看似安全,但如果 config2 里某个属性是 null 或 undefined,它仍会把 undefined 赋给目标对象对应字段,导致意外清空值。
- 所有源对象中同名属性,以最右侧出现的那个为准
-
undefined和null会被正常复制过去(不会跳过) - 原始类型和引用类型都按值复制:对引用类型只拷贝指针,不深拷贝
- 不可枚举属性(如
Object.defineProperty设置的enumerable: false)会被忽略
浅合并下嵌套对象不会被递归处理
如果配置项里有 api、theme 这类嵌套对象,Object.assign 只替换整个 api 属性,不会把 config1.api.timeout 和 config2.api.baseURL 合并起来。这是最容易误用的点。
示例:
const base = { api: { timeout: 5000 } };
const overrides = { api: { baseURL: 'https://api.example.com' } };
const result = Object.assign({}, base, overrides);
// result.api === { baseURL: 'https://api.example.com' }
// 注意:timeout 字段已经丢失
- 嵌套对象被视为一个整体,整块被替换
- 想保留原嵌套结构中的部分字段?得手动拆解或换用深合并工具
- 若源对象某嵌套属性为
undefined,对应目标位置也会变成undefined,不是“保持原值”
避免意外修改原始配置对象
直接写 Object.assign(config1, config2) 会让 config1 被改写。多数场景下你不希望原始默认配置被污染,尤其当它被多处复用时。
- 总是用空对象作为第一个参数:
Object.assign({}, defaults, userConfig) - 不要依赖
Object.assign的返回值去判断是否“新建了对象”——它只是返回第一个参数的引用 - 如果
defaults里有函数或正则等特殊对象,它们仍是浅拷贝,修改其内部状态会影响所有引用方
兼容性与替代方案的取舍
Object.assign 在 IE11 及以上、Node.js 4+ 均可用;但如果你需要真正合并嵌套字段,就得自己封装或引入 lodash.merge。不过要注意:引入第三方库可能只为解决一个配置合并问题,未必划算。
- 纯浅合并 + 明确知道无嵌套 →
Object.assign足够轻量 - 有深层结构且字段分散在不同来源 → 必须手写递归逻辑或用
merge类函数 - 使用 TypeScript 时,
Object.assign的类型推导对多参数支持较弱,可能需显式标注返回类型
嵌套层级越深,越容易忽略浅合并的破坏性;实际项目中,建议把“哪些字段允许被覆盖”“哪些必须保留默认”写进文档,而不是靠开发者记住 Object.assign 的行为边界。










