In JavaScript, when does a default parameter value actually apply, and what happens if the caller passes null?
answer
- one trigger only
- missing and explicit are the same
- null is a value, not an absence
- falsy is not the test
- undefined only, never null or zero
basics
~20 sA default applies only when the argument is undefined — omitted, or explicitly passed as undefined. Every other value, including null, 0, empty string, false and NaN, is used as-is, so null never triggers the default.
solid answer
~40 sDefault parameter values in JavaScript are triggered by exactly one condition: the argument's value is `undefined`. That covers both an omitted argument and an explicitly passed `undefined`, which is why `f()` and `f(undefined)` behave identically. Anything else is passed through untouched, so `null`, `0`, `''`, `false` and `NaN` all suppress the default. This is the key difference from the older `||` idiom — `function f(x) { x = x || 10 }` also replaces `0` and `''` because those are falsy, while `function f(x = 10) {}` only replaces `undefined`. If you genuinely want `null` to fall back too, either use `??` inside the body or normalise the argument yourself; the parameter default alone will not do it.
code
javascript · 9 linesfunction greet(name = 'guest') {
return `hello ${name}`;
}
console.log(greet()); // hello guest
console.log(greet(undefined)); // hello guest
console.log(greet(null)); // hello null
console.log(greet(0)); // hello 0
console.log(greet('')); // hellogo deeper
Be ready to say plainly that the default applies only when the argument is undefined, and that null, 0 and empty string are passed through unchanged. Naming the || idiom as the buggy older alternative is a strong extra.
Explain the mechanics: each parameter is bound, then any parameter still holding undefined has its initialiser evaluated, lazily and per call. Contrast ||, ?? and parameter defaults on the exact value sets each one replaces.
Show the production angle — a default that swallows 0 or '' is a silent data bug, and forwarding an unset variable between functions is how explicit undefined reaches a callee in real code. Say how you would make null mean "use the default" when an external API sends it.
Own the API-shape decision: positional optionals force callers to pass undefined as a placeholder, which is the point at which you move to an options object. Argue for a single, documented convention across a codebase for how absence is expressed rather than mixing ||, ?? and defaults per function.
## What a default parameter is Since ES2015 a function parameter can carry an initialiser directly in the signature: ```js function greet(name = 'guest') { return `hello ${name}`; } ``` The expression after `=` is the *default value*. It is not applied unconditionally: the runtime binds each parameter to the corresponding argument, and then, for each parameter that has an initialiser, checks whether the bound value is `undefined`. Only in that case is the initialiser evaluated and its result assigned. ## The single trigger: undefined The rule has no exceptions worth remembering beyond the one word: **undefined**. Two situations produce it. 1. The argument is missing. `greet()` passes no argument, so `name` starts out `undefined`. 2. The argument is present but its value is `undefined`. `greet(undefined)` is indistinguishable from `greet()` as far as the default is concerned. Everything else is a real value and is kept: ```js greet(); // 'hello guest' greet(undefined); // 'hello guest' greet(null); // 'hello null' greet(0); // 'hello 0' greet(''); // 'hello ' greet(false); // 'hello false' greet(NaN); // 'hello NaN' ``` That `greet(null)` line is where candidates most often guess wrong. `null` is a deliberate "no value" *value*; the language treats it as data the caller chose to send, not as an absence. ## Why this differs from the `||` idiom Before defaults existed, the common pattern was: ```js function area(width) { width = width || 100; // pre-ES2015 idiom return width * width; } area(0); // 10000 — the zero was thrown away ``` `||` tests *falsiness*, so it swallows `0`, `''`, `false`, `NaN` and `null` along with `undefined`. That is a real bug class: a user-supplied `0` silently becomes the fallback. Parameter defaults test only `undefined`, which is almost always the behaviour you meant. The nullish coalescing operator `??` (ES2020) sits between the two — it falls back for `undefined` *and* `null`, but not for other falsy values: ```js function area(width) { const w = width ?? 100; // 0 survives, null does not return w * w; } ``` So if your API wants `null` to mean "use the default", combine the two: keep the parameter default for the omitted case and normalise `null` in the body, or drop the parameter default and use `??` alone. ## Interaction with positional arguments Because a default only fires on `undefined`, `undefined` becomes the idiomatic "skip this one" placeholder when you want a later positional argument: ```js function slice(source, start = 0, end = source.length) { return source.slice(start, end); } slice('abcdef', undefined, 3); // 'abc' — start falls back to 0 ``` Passing `null` there would give `start === null`, which coerces to `0` in the arithmetic but is a different value entirely and would break a parameter whose default is a non-numeric object. This awkwardness is the usual argument for switching to a destructured options object once a function has more than two or three optional inputs. ## A default is an expression, evaluated per call The initialiser is a full expression, not a literal, and it is re-evaluated on every call that needs it. `function log(msg, at = Date.now()) {}` stamps a fresh timestamp each time, and `function collect(item, into = [])` creates a brand-new array per call, so calls never share state through the default. The expression can also call a function, which gives the standard "required argument" trick: ```js const required = (name) => { throw new TypeError(`${name} is required`); }; function save(record = required('record')) { return record; } save(); // TypeError: record is required save(null); // returns null — null is a value, so nothing throws ``` That last line is the same lesson again: the guard only fires on `undefined`. ## What an interviewer is checking They want to hear the word `undefined` used precisely, the explicit statement that `null` and `0` are preserved, and ideally the contrast with `||` and `??`. Saying "the default kicks in when the argument is missing" is only half right — it misses `f(undefined)`, which is the case that actually shows up in real code when one function forwards an unset variable to another.
- If you want null to fall back to the default as well, how do you express that?The parameter default cannot do it, so normalise inside the body with nullish coalescing: `function f(x) { const v = x ?? 10; }`. `??` treats both `undefined` and `null` as absent while preserving `0`, `''` and `false`. Keeping the parameter default *and* a `??` in the body is redundant — pick one place to define the fallback so readers do not have to reconcile two.
- Does a default parameter change what arguments.length reports?No. `arguments.length` is the count of arguments the caller actually passed, before any default is applied. `function f(a = 1) {}` called as `f()` sees `arguments.length === 0` even though `a` is `1`. That is a reliable way to distinguish "omitted" from "explicitly passed undefined" if an API really needs to, though needing that distinction usually signals the signature should change.
- Is the default expression evaluated even when the caller supplies a value?No. The initialiser is evaluated lazily, only for the calls where the parameter is `undefined`. So `function f(x = expensive())` never runs `expensive()` when a value is passed, and a default like `throw`-ing a required-argument helper is safe to leave in the signature — it only fires on the calls that omit the argument.
saying these in an interview costs you the question
- Says the default fires for any falsy argument
- Claims null triggers the default like undefined does
- Treats a parameter default as equivalent to x = x || fallback
- Thinks the default only applies when the argument is omitted, not when undefined is passed
- Assumes the default expression runs on every call regardless