展开运算符仅支持浅层合并,无法深度覆盖嵌套对象;需结合递归deepmerge函数实现优雅深度合并,支持数组策略与解构默认值。

用展开运算符(...)做深度覆盖默认配置,单靠它本身做不到——它只做浅层合并。要“优雅”实现深度覆盖,得把展开运算符作为基础工具,再配合递归合并逻辑。
为什么展开运算符不能直接深度覆盖
展开运算符对对象是浅拷贝:遇到嵌套对象时,它不会递归遍历子属性,而是直接替换整个子对象。
例如:
const defaults = { api: { url: '/api', timeout: 5000 }, debug: false };
const config = { api: { url: '/v2/api' } };
const merged = { ...defaults, ...config };
// 结果:{ api: { url: '/v2/api' }, debug: false }
// ❌ timeout 被丢掉了 —— 因为整个 defaults.api 被 config.api 替换了
优雅方案:用展开运算符 + 递归合并函数
保留展开语法的简洁性,把深度逻辑封装进一个纯函数,调用时仍保持声明式风格。
示例实现(TypeScript/JS 都适用):
function deepMerge(target, source) {
if (typeof target !== 'object' || target === null || typeof source !== 'object' || source === null) {
return source;
}
const output = { ...target }; // 浅拷贝目标,避免污染
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
output[key] = deepMerge(output[key], source[key]);
}
}
return output;
}
// 使用方式(依然用展开运算符组织参数)
const finalConfig = deepMerge(defaults, userConfig);
✅ 优势:调用干净、可复用、支持任意嵌套层级、不依赖外部库。
进阶技巧:支持数组合并策略(如 concat 或覆盖)
默认 deepMerge 把数组当普通对象处理(逐项递归),但有时你希望 features: ['a'] 和 features: ['b'] 合并成 ['a','b']。
只需在 deepMerge 中加一层判断:
- 检测到双方都是数组 → 按需
concat或用[...target, ...source] - 或提供选项:
deepMerge(defaults, config, { array: 'concat' })
这样既保持展开运算符的表达力,又让深度逻辑可配置。
实用建议:结合解构 + 默认值提升可读性
在函数接收配置时,可先深合并在解构,让默认值语义更清晰:
function createService(options = {}) {
const config = deepMerge(
{ api: { url: '/api', timeout: 5000 }, debug: false },
options
);
const { api: { url, timeout }, debug } = config; // 解构即带默认
// ...
}
比层层 options.api?.url ?? defaults.api.url 更简洁、更易维护。










