在 JavaScript 里,函数参数默认值是处理“调用时没给参数”最直接的办法。它从 ES6 开始可用,能让函数定义本身就带上兜底规则;但它到底在什么情况下生效、遇到 null 或 0 时会不会替换、和解构参数一起用时又该怎么写,恰恰是最容易混淆的地方。下面按实际使用场景拆开说明,方便你快速判断该直接写默认值,还是改用 ?? 或显式判断。
直接在形参里写默认值
从 ES6 开始,JavaScript 可以在函数定义阶段直接给参数赋默认值,写法就是在形参后面接 =。这样函数在没有拿到对应参数时,会自动使用预设值,不需要再在函数体里手动判断。

function greet(name = '游客', age = 18) {
console.log(`你好,${name},今年${age}岁`);
}
greet(); // 你好,游客,今年18岁
greet('小明'); // 你好,小明,今年18岁
greet('小红', 25); // 你好,小红,今年25岁
这里有一个判断规则要记住:只有参数值为 undefined 时,默认值才会生效。所谓“没传参”,本质上也是该位置得到 undefined。
因此,下面这些情况要区分开看:
- 不传参:会触发默认值
- 显式传入
undefined:也会触发默认值 - 传入
null、0、false、空字符串:不会触发默认值,函数会直接使用传入值
这也是参数默认值和很多人熟悉的“假值兜底”写法最大的不同。默认值机制判断的是 undefined,不是广义上的 falsy 值。
默认值也可以是动态表达式
默认值不一定只能写固定字面量,它也可以是表达式、函数调用,甚至依赖外部变量。这样做的好处是,默认值不仅能“补空”,还能在函数调用时动态计算。
function createId(prefix = 'item_', suffix = Date.now()) {
return `${prefix}${suffix}`;
}
console.log(createId()); // item_171xxxxxx
function log(msg, time = new Date().toLocaleTimeString()) {
console.log(`[${time}] ${msg}`);
}
这里的关键点是:默认值表达式会在每次调用函数时重新执行,而不是函数定义时只执行一次。所以像 Date.now() 这样的表达式,每次调用拿到的结果都可能不同。
这类写法适合用于:
- 生成时间戳、编号、随机值等动态内容
- 按当前环境补充日志时间、上下文信息
- 根据外部变量或已有参数生成兜底值
但也要注意,既然表达式会在调用时执行,就不适合放入成本过高、带明显副作用的逻辑。默认值应当保持可预期,避免让函数签名本身变得难理解。
解构参数时,记得给整体参数一个默认值
当函数接收的是对象或数组时,默认值通常会和解构赋值一起使用。这是前端开发里很常见的一种模式,尤其适合配置项函数。

function connect({ host = 'localhost', port = 3000, secure = false } = {}) {
const url = `${secure ? 'https' : 'http'}://${host}:${port}`;
console.log(url);
}
connect(); // http://localhost:3000
connect({ host: 'api.example.com', secure: true }); // https://api.example.com:3000
这段代码里其实有两层默认值:
- 对象内部的
host、port、secure分别有自己的默认值 - 整个参数最后的
= {},给“整个对象参数”提供了一个默认空对象
第二层尤其重要。因为如果调用时完全不传参数,函数拿到的是 undefined,这时直接解构会报错;补上 = {} 后,解构就有了安全的起点。
可以把它理解成两步:
- 先判断有没有传入对象,没有就先用空对象
{} - 再从这个对象里解构字段,没有的字段再各自使用默认值
这种写法比在函数体里层层判断更紧凑,语义也更清楚,特别适合可选配置较多的场景。
常见误区:null 和其他 falsy 值不会触发默认值
很多人第一次接触参数默认值时,会误以为它和 || 兜底差不多,实际上两者规则不同。参数默认值只认 undefined,而 null、0、false、'' 都会被当成“用户明确传入了值”。

function showCount(count = 10) {
console.log(count); // 传 0 → 输出 0,不是 10
}
function showCountSafe(count) {
count = count ?? 10;
console.log(count); // 传 0 或 null → 输出 0 或 null;只对 undefined 用 10
}
这里还需要顺手澄清一个容易写错的判断思路:
||不适合做严格兜底,因为它会把0、false、空字符串也当成需要替换的值??更适合做“空值判断”,只会在左侧是null或undefined时才使用右侧值- 如果你的规则必须做到完全可控,也可以回到显式判断:
if (param === undefined) param = defaultValue;
也就是说,什么时候用哪种写法,取决于你的业务语义:
- 只想处理“没传”或“明确传入 undefined”:直接用参数默认值
- 想把
null也视为缺失值:优先考虑?? - 需要自定义更细的规则:在函数体里手动判断最稳妥
实际使用时怎么选更合适
如果只是给函数提供一个清晰的默认输入,参数默认值通常就是最简洁的方案。它把“函数在缺参时怎么处理”直接写进了签名,调用方和维护者都能一眼看懂。
但在下面几种情况下,最好多做一步判断:
- 你需要区分“未传参”和“传入 null”
- 你希望
0、false、空字符串保留原值,不能被误替换 - 参数是对象解构,且函数可能被空调用
从实战角度看,可以直接记住这条经验:
- 简单缺参兜底,用形参默认值
- 涉及空值语义,用
?? - 涉及复杂规则或解构安全,补上整体默认值,必要时再做显式判断
掌握这几个边界后,JavaScript 的函数默认值就不只是“少写几行 if”,而是能把参数规则写得更直观、更少歧义。













