Object Default Parameter
ID |
javascript.object_default_parameter |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Code Smell |
Language |
JavaScript |
Tags |
code-style, parameters, readability |
Description
Reports an object or array literal written as a parameter’s default value.
A destructuring parameter’s own default (function f({a} = {})) is not reported: there the empty literal is what makes the argument optional at all, and no other spelling does the same job.
Rationale
// Bad
function subscribe(topic, options = { retries: 3, timeout: 30 }) {
return client.on(topic, options);
}
-
Unlike Python, JavaScript evaluates a default expression on every call in which the argument is missing, so the shared-mutable-default bug of other languages does not exist here. The mirror image of it does: each call that omits the argument gets a different object, equal to the last one but not identical to it.
-
Every consumer that compares by identity therefore sees a change that did not happen — a
===guard, a cache or memo keyed on the value, a React dependency array, an equality check in a store. The symptom is work redone on every call, and it gets attributed to the consumer rather than to the signature. -
A literal in the signature is also where configuration hides: the defaults are spread across every function that takes them, so changing one means finding all of them, and a caller reading the call site cannot see what it is getting.
Remediation
Hoist the literal to a shared frozen constant, so every call that omits the argument gets the same reference — and the defaults have one place to live.
// Good
const DEFAULT_OPTIONS = Object.freeze({ retries: 3, timeout: 30 });
function subscribe(topic, options = DEFAULT_OPTIONS) {
return client.on(topic, options);
}
// Good — the destructuring default stays; it is what makes the argument optional
function render({ width = 0, height = 0 } = {}) {
return width * height;
}