悠悠楠杉
JavaScript的Object.assign方法详解:深拷贝还是浅拷贝?
一、什么是Object.assign?
Object.assign()
是ES6引入的一个对象操作方法,用于将一个或多个源对象(source)的可枚举属性复制到目标对象(target)。其基本语法为:
javascript
Object.assign(target, ...sources)
当我们需要合并多个对象时,这个方法显得尤为实用。比如在React/Vue的状态管理、配置对象合并等场景中经常能看到它的身影。
二、核心特性与使用示例
1. 基础用法
javascript
const target = { a: 1 };
const source = { b: 2 };
const result = Object.assign(target, source);
console.log(result); // { a: 1, b: 2 }
console.log(target === result); // true
注意点:
- 方法会修改第一个参数(目标对象)
- 返回值为修改后的目标对象
- 同名属性会被后续源对象覆盖
2. 多对象合并
javascript
const user = { name: 'Alex' };
const job = { position: 'FE Developer' };
const company = { name: 'TechCorp' };
const employee = Object.assign({}, user, job, company);
// { name: 'TechCorp', position: 'FE Developer' }
3. 只复制可枚举属性
javascript
const obj = Object.create({ inheritProp: '不可枚举' }, {
enumerableProp: {
value: '可枚举',
enumerable: true
}
});
console.log(Object.assign({}, obj));
// { enumerableProp: '可枚举' }
三、关键问题深度解析
1. 浅拷贝的本质
Object.assign执行的是浅拷贝(shallow copy),当属性值为引用类型时,复制的是引用地址:
javascript
const obj1 = { nested: { value: 1 } };
const obj2 = Object.assign({}, obj1);
obj2.nested.value = 2;
console.log(obj1.nested.value); // 2 (原始对象也被修改)
2. 与扩展运算符的对比
javascript
// 等效写法
const cloneA = Object.assign({}, obj);
const cloneB = { ...obj };
// 细微差别:
// - Object.assign会触发setter,而扩展运算符不会
// - 扩展运算符在TypeScript中有更好的类型推断
3. 特殊值处理规则
| 输入类型 | 处理方式 |
|----------------|-------------------------|
| undefined/null | 被忽略 |
| 原始值 | 转为包装对象后复制属性 |
| Symbol属性 | 可以被正常复制 |
javascript
Object.assign({}, undefined); // 不报错,返回{}
Object.assign({}, 123); // {}
Object.assign({}, 'abc'); // {0:'a', 1:'b', 2:'c'}
四、实战应用场景
1. 默认参数覆盖
javascript
function init(config) {
const defaults = { duration: 300, easing: 'linear' };
return Object.assign({}, defaults, config);
}
2. 不可变数据模式
javascript
// Redux reducer中的状态更新
function reducer(state, action) {
return Object.assign({}, state, {
loading: false,
data: action.payload
});
}
3. 原型链属性复制
javascript
function cloneProto(instance) {
return Object.assign(
Object.create(Object.getPrototypeOf(instance)),
instance
);
}
五、局限性及替代方案
虽然Object.assign很方便,但在以下场景需要注意:
- 深嵌套对象:需要配合递归或使用库如lodash.cloneDeep
- 非可枚举属性:需要使用Object.getOwnPropertyDescriptors
- 性能敏感场景:大数据量时考虑专有方案
javascript
// 深拷贝替代方案
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
// 现代浏览器支持的新API
const deepClone = structuredClone(obj);
六、最佳实践建议
总是以空对象作为第一个参数来避免副作用javascript
// 不推荐
Object.assign(existingObj, newProps);// 推荐
const newObj = Object.assign({}, existingObj, newProps);对TypeScript项目,考虑使用类型安全的变体:
typescript function safeAssign<T extends object>(target: T, ...sources: Partial<T>[]): T { return Object.assign(target, ...sources); }
在需要保留原型链时,组合使用Object.create:
javascript function clone(obj) { return Object.assign( Object.create(Object.getPrototypeOf(obj)), obj ); }