JavaScript 解构默认值:已经写了备用值,为什么 null 仍然原样留下
读取任务选项时,给 retries 写了默认值三,接口传来 null 后却依旧得到 null。这里的默认初始化只在取到 undefined 时执行;它没有检查值是否适合业务,也不会把所有看起来“空”的输入换成三。
下面把整个参数省略、字段为 undefined,以及 null、零、false、空字符串分别保留下来。保存为 demo.mjs,运行 node demo.mjs;全部操作发生在内存中,fallback 的计数让默认表达式是否执行也能被观察。
AI模型生成概念示意:只有缺失通道接入备用块,其他已有值沿原通道通过;不是程序运行截图。
import assert from 'node:assert/strict';
let defaults = 0;
const fallback = () => { defaults++; return 3; };
function readOptions({ retries = fallback() } = {}) {
return retries;
}
const cases = [
['omitted', undefined, 3],
['undefined', { retries: undefined }, 3],
['null field', { retries: null }, null],
['zero', { retries: 0 }, 0],
['false', { retries: false }, false],
['empty', { retries: '' }, ''],
];
for (const [name, input, expected] of cases) {
const actual = readOptions(input);
assert.equal(actual, expected);
console.log(name + ': ' + JSON.stringify(actual));
}
assert.equal(defaults, 2);
console.log('default calls: ' + defaults);
assert.throws(() => readOptions(null), TypeError);
console.log('null argument: TypeError');
const source = {};
const { retries: attempts = 5 } = source;
assert.equal(attempts, 5);
assert.equal(Object.hasOwn(source, 'retries'), false);
console.log('local value: ' + attempts + ', source field: absent');先读字段,再决定是否计算默认表达式
前两行是 omitted: 3 和 undefined: 3。第一次调用没有提供整个参数,外层的 = {} 先提供空对象,再由字段默认值补三。第二次已经有对象,但 retries 的读取结果为 undefined,同样触发 fallback。默认调用次数因此是二。
随后输出 null field: null、zero: 0、false: false、empty: ""。这四个值虽然可能在某些业务中被视为无效,但都不是 undefined。默认表达式也不会为了“先准备着”而额外计算,计数保持二。如果备用值计算昂贵,这个延迟求值行为很有用;若它有外部副作用,则必须单独管理。
整个对象的缺省与字段的缺省是两层规则
调用 readOptions(null) 会产生 TypeError,代码通过 assert.throws 捕获并验证。外层参数默认值同样只处理 undefined,所以 null 没有先变成空对象。接下来的对象解构试图从 null 读取字段,便在进入函数体之前失败。给字段加再多默认值,也不能修补这一层。
若接口约定 null 表示“没有选项”,可以先在入口明确将它归一为对象;若 null 表示用户主动清空,就应保留其含义。不要凭一次异常顺手把所有假值都改为空对象,否则零、false 与空字符串也会被悄悄吞掉。外部输入还应检查类型与允许的字段。
默认值写进局部绑定,不会回填原对象
最后一行是 local value: 5, source field: absent。冒号把来源字段 retries 绑定到本地变量 attempts,等号为这个本地绑定提供备用值。断言确认 source 仍然没有 retries 属性。因而不能把解构当作给配置对象补齐字段的原地更新工具。
本例只是展示语言规则,所以故意允许 null、false 等值返回。真正的重试次数接口通常还需要整数、非负数与上限校验。建议将输入归一、字段缺省和业务验证写成可分别测试的步骤,至少保留上述六类输入以及整个参数为 null 的样本。
资料核对日期:2026年10月2日。完整代码在 Node.js v24.19.0 实际运行,所有断言通过;输出仅对应文中固定输入。


