object.assign 不引发隐式类型转换,仅执行属性值直接赋值;其行为误解多源于误用原始值作 target/source、忽略 undefined/null 覆盖语义、混淆浅/深合并、遗漏 symbol 或不可枚举属性处理。

Object.assign 本身不会引发隐式类型转换——它只做属性值的直接赋值,不涉及 +、== 或条件判断等触发隐式转换的上下文。所谓“隐式类型转换问题”,实际多源于开发者误用或对行为理解偏差,比如把非对象传入、依赖 undefined 的“空值跳过”逻辑,或混淆了浅合并与深合并语义。
别把原始值当 target 或 source
target 若为 null 或 undefined,会直接抛 TypeError;若为字符串、数字等原始值,会被强制转为包装对象(如 "abc" → {0:'a',1:'b',2:'c'}),这看似像转换,实则是规范定义的 ToObject 操作,不是运行时隐式转换。source 为 null 或 undefined 则被静默忽略。
- ✅ 安全写法:
Object.assign({}, obj1, obj2)—— target 明确是空对象 - ❌ 危险写法:
Object.assign('default', {a: 1})—— 得到奇怪的索引对象,且不可预期
不要指望 undefined/null 自动跳过
如果某个 source 对象里某字段值是 undefined 或 null,Object.assign 会原样复制过去,覆盖 target 中原有值。这不是类型转换,但效果常被误认为“清空了字段”。
- 例如:
Object.assign({x: 'old'}, {x: undefined})→{x: undefined} - 若想跳过空值,需预处理:
const clean = obj => Object.fromEntries(Object.entries(obj).filter(([,v]) => v != null)),再传入
嵌套对象不合并,整块替换 —— 这不是转换,是语义误解
很多人以为 api: { timeout: 5000 } 和 api: { baseURL: '...' } 合并后能保留两者,结果得到的 api 只有 baseURL。这不是类型变了,而是 Object.assign 压根不深入嵌套结构,它只做一层 key-value 赋值。
- 若需深层合并,必须换方案:手写递归、用
lodash.merge,或对嵌套字段单独处理 - 例如:
Object.assign({}, base, { api: { ...base.api, ...overrides.api } })
注意 Symbol 属性和不可枚举属性的边界
Object.assign 只复制可枚举的自有属性,包括可枚举的 Symbol 属性;但会跳过 Object.defineProperty 设置的 enumerable: false 属性,也完全不处理原型链上的属性。这容易让人误以为“某些值丢失了”,其实只是没被选中复制。
- 检查是否可枚举:
Object.keys(obj)或Object.getOwnPropertyNames(obj)对比即可确认 - 需要复制不可枚举属性?得用
Object.getOwnPropertyDescriptors+Object.defineProperties











