Object.assign不支持渐进式覆盖,仅浅拷贝可枚举自有属性并后覆盖前;可通过过滤空值、白名单控制或深度递归实现渐进合并。

Object.assign 本身不支持“渐进式覆盖”(即按需合并、跳过某些属性、或对同名属性做自定义处理),它只是简单地将源对象的**可枚举自有属性**从左到右依次复制到目标对象,后复制的会直接覆盖前面的值。但你可以通过组合策略,模拟出“渐进式覆盖”的效果——比如保留已有值、仅覆盖空值/undefined、或按字段白名单控制合并行为。
1. 基础用法:理解默认覆盖逻辑
Object.assign(target, ...sources) 是浅拷贝,同名属性会被后续源对象的值无条件覆盖:
const base = { name: 'Alice', age: 25, city: 'Beijing' };
const update = { age: 26, city: undefined, job: 'Engineer' };
Object.assign(base, update);
// → { name: 'Alice', age: 26, city: undefined, job: 'Engineer' }
注意:city: undefined 也被写入了,这通常不是“渐进式”想要的效果(我们往往希望 undefined 不覆盖原有值)。
2. 过滤空值:只覆盖非 undefined / null 的有效值
用 Object.entries() + reduce() 手动构建目标对象,跳过无效值:
function assignIfDefined(target, ...sources) {
return sources.reduce((acc, src) => {
Object.entries(src).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
acc[key] = value;
}
});
return acc;
}, { ...target });
}
const result = assignIfDefined(
{ name: 'Bob', age: 30, city: 'Shanghai' },
{ age: undefined, city: 'Hangzhou', job: 'Designer' }
);
// → { name: 'Bob', age: 30, city: 'Hangzhou', job: 'Designer' }
3. 白名单控制:仅允许特定字段被覆盖
适合配置合并场景,防止意外覆盖关键字段:
function assignWithWhitelist(target, whitelist, ...sources) {
const result = { ...target };
sources.forEach(src => {
whitelist.forEach(key => {
if (src.hasOwnProperty(key)) {
result[key] = src[key];
}
});
});
return result;
}
const config = { timeout: 5000, retries: 3, debug: false };
assignWithWhitelist(config, ['timeout', 'retries'], { timeout: 8000, debug: true });
// → { timeout: 8000, retries: 3, debug: false } —— debug 被忽略
4. 深度渐进合并(浅层不够?)
Object.assign 是浅拷贝,无法处理嵌套对象的渐进更新。如需深度能力,推荐使用 lodash.merge 或手写递归 merge 函数。但若坚持用原生,可封装一个轻量版:
function deepAssignIfDefined(target, ...sources) {
if (!sources.length) return target;
const source = sources[0];
const rest = sources.slice(1);
for (const key in source) {
if (!source.hasOwnProperty(key)) continue;
const val = source[key];
if (val != null && typeof val === 'object' && !Array.isArray(val) && typeof target[key] === 'object') {
target[key] = deepAssignIfDefined(target[key], val);
} else if (val !== undefined && val !== null) {
target[key] = val;
}
}
return rest.length ? deepAssignIfDefined(target, ...rest) : target;
}
这样就能实现类似 { user: { name: 'A', settings: { theme: 'dark' } } } 中只更新 theme 而不抹掉 name 的效果。










