null vs undefined
When JS uses undefined vs null, equality traps, defaults, JSON gaps, and how to choose one intentionally in APIs.
- javascript
- null-vs
Both mean “no value,” but they are not the same type and the language treats them differently. Interviews love == vs ===, optional params, and “what does missing JSON key become?”
Definitions
undefined |
null |
|
|---|---|---|
| Type | 'undefined' |
'object' (legacy bug) |
| Who sets it | Language / missing things | Usually you (or APIs) |
| Meaning | Not initialized / absent | Intentional empty |
let x;
x; // undefined — declared, not assigned
function f(a) {
return a; // call f() → a is undefined
}
const o = {};
o.missing; // undefined
const u = { name: null }; // explicit empty name
Equality
null == undefined; // true (loose)
null === undefined; // false
null == 0; // false
undefined == 0; // false
Use === unless you deliberately want “nullish” with == null:
if (value == null) {
// true for null OR undefined
}
// same intent:
if (value === null || value === undefined) {}
Prefer ?? / optional chaining over == null when assigning defaults:
const port = config.port ?? 3000; // only null/undefined → 3000
|| also replaces 0 and '' — usually wrong for numeric defaults.
JSON and the wire
JSON.stringify({ a: undefined, b: null });
// '{"b":null}' — undefined keys are dropped on objects
JSON.stringify([1, undefined, null]);
// '[1,null,null]' — array holes become null in JSON
JSON.parse never produces undefined as a value; absence is missing keys.
typeof traps
typeof undefined; // 'undefined'
typeof null; // 'object' ← memorize
Never use typeof x === 'object' alone to mean “plain object”; exclude null.
API design choice
Convention that scales:
undefined: property omitted, param not passed, “use default.”null: field exists, value is explicitly empty (DB NULL, cleared form field).
// PATCH body: omit = leave unchanged; null = clear
function patchUser(id, { name, bio }) {
const body = {};
if (name !== undefined) body.name = name;
if (bio !== undefined) body.bio = bio; // bio: null clears
return fetch(`/users/${id}`, {
method: 'PATCH',
body: JSON.stringify(body),
});
}
Defaults and parameters
function greet(name = 'world') {
// default only if name === undefined (not null!)
return `hi ${name}`;
}
greet(); // hi world
greet(undefined);// hi world
greet(null); // hi null
Interview answer (out loud)
“
undefinedmeans missing or uninitialized — the language produces it for missing args and properties.nullis an intentional empty object value. They’re loosely equal but not strictly. Prefer===, use??for nullish defaults, and remembertypeof null === 'object'and that JSON dropsundefinedobject keys.”
Destructuring defaults
const { name = 'anon' } = {}; // name is 'anon' (missing → undefined → default)
const { name: n = 'anon' } = { name: null }; // n is null — default skipped
Same rule as function parameter defaults: only undefined triggers them.
void 0
void 0 === undefined; // true
// historical: protect against undefined being shadowed as a variable in sloppy mode
Modern modules make shadowing undefined a non-issue; still appears in compiled output sometimes.
Further reading
Related
Related guides
- AbortControllerCancel fetch and other async work with AbortController and AbortSignal — timeouts, race conditions, and cleanup when components unmount.
- Array find, some, every, includesShort-circuiting array predicates: find, findIndex, some, every, and includes — when to use each and common interview traps.
- Array map, filter, reducemap, filter, and reduce as the core transform toolkit — immutability, chaining costs, reduce patterns, and when a plain loop is clearer.
- Arrow Functions Deep DiveLexical this, no arguments object, no construct, concise bodies — when arrows help and when methods and generators need classic functions.
- Async Iteration and for await...ofAsync iterables, for await...of, and streaming data — how async iterators differ from Promise.all and when to use each.