展开运算符(...)配合空值合并(??)可安全填充对象缺失字段,保留0、''、false等合法假值;支持嵌套结构(?.+??)、批量填充工具函数,且避免解构默认值的局限性。

直接用展开运算符(...)配合空值合并(??),能安全地填充对象副本的缺失字段,同时保留所有“合法假值”如 0、''、false —— 这是传统 || 做不到的关键优势。
基础组合写法:展开 + ?? 保障字段存在性
当你要创建一个新对象副本,并确保某些属性有默认值,但又不想覆盖已存在的有效值(包括 0 或空字符串),推荐这样写:
- 先用
{...original}浅拷贝原始对象 - 再用
??显式指定哪些字段允许 fallback,默认只在null/undefined时生效 - 最后把带默认值的字段作为独立键写入新对象中
示例:
const original = { name: '', count: 0, active: false };
const defaults = { name: 'Anonymous', count: 100, active: true };
const safeCopy = {
...original,
name: original.name ?? defaults.name,
count: original.count ?? defaults.count,
active: original.active ?? defaults.active
};
// 结果:{ name: '', count: 0, active: false } —— 全部原始值都被保留
嵌套结构处理:?. 和 ?? 联合使用
若原始对象含深层属性(如 user.profile.avatar.url),单独展开无法自动补全中间层级。这时需结合可选链 ?. 提取值,再用 ?? 设默认:
-
user?.profile?.avatar?.url ?? '/default-avatar.png'安全取值且不报错 - 该表达式结果可直接用于展开后的对象字面量中
- 避免手动判断
user && user.profile && user.profile.avatar等冗长逻辑
示例:
const user = { profile: { name: 'Alex' } };
const filled = {
...user,
avatarUrl: user?.profile?.avatar?.url ?? '/placeholder.jpg',
displayName: user?.profile?.name ?? 'Guest'
};
// → { profile: { name: 'Alex' }, avatarUrl: '/placeholder.jpg', displayName: 'Alex' }
批量填充工具函数:复用性强且语义清晰
对多个字段统一应用默认策略时,封装一个辅助函数更可靠:
- 接收原始对象、默认配置对象、以及要填充的键名数组
- 遍历键名,对每个键执行
obj[key] ?? defaults[key] - 返回融合后的新对象,不影响原对象
代码示意:
function fillDefaults(obj, defaults, keys) {
return {
...obj,
...Object.fromEntries(
keys.map(key => [key, obj[key] ?? defaults[key]])
)
};
}
// 使用
const data = { id: 123, title: null, tags: [] };
const result = fillDefaults(data, { title: 'Untitled', tags: ['misc'] }, ['title', 'tags']);
// → { id: 123, title: 'Untitled', tags: [] }(tags 未被覆盖,因它不是 null/undefined)
注意事项:避免与解构默认值混淆
不要误用解构赋值中的默认值语法({ name = 'Default' } = obj)替代 ??,因为:
- 解构默认值在
obj.name为undefined时才触发,但若obj本身不存在,会直接报错 - 它不能用于动态键或嵌套路径,也不支持与展开混合后精准控制单个字段
-
??是运行时求值,适用范围更广,且与?.天然协同










