Undefined vs Null in JavaScript: Key Differences Explained
Master the differences between undefined and null in JavaScript. Learn when each value appears, how they behave in comparisons and type checks, and which to use in your own code.
JavaScript has two distinct values that represent "nothing": undefined and null. Both indicate the absence of a meaningful value, but they serve different purposes, behave differently in comparisons, and appear in different situations. Mixing them up is one of the most common sources of bugs for JavaScript developers.
Think of undefined as an empty parking spot that nobody has claimed yet. The spot exists, but nobody parked there. null is a parking spot where someone deliberately placed a "Reserved" cone. The spot is intentionally empty. In JavaScript, undefined means "this has not been given a value yet" while null means "this has been explicitly set to nothing."
This guide covers every difference between undefined and null, including type checking, equality comparisons, type coercion, and clear guidelines for when to use each in your code.
When JavaScript Produces undefined
JavaScript automatically assigns undefined in several situations. You do not have to write it yourself for these cases:
// 1. Declared variable with no assignment
let username;
console.log(username); // undefined
// 2. Missing function parameters
function greet(name) {
console.log(name);
}
greet(); // undefined
// 3. Function with no return statement
function doNothing() {
// no return
}
console.log(doNothing()); // undefined
// 4. Accessing a non-existent object property
const user = { name: "Alice" };
console.log(user.age); // undefined
// 5. Array elements that don't exist
const colors = ["red", "blue"];
console.log(colors[5]); // undefined
// 6. void operator
console.log(void 0); // undefinedundefined is JavaScript's Default Empty Value
When JavaScript encounters a variable, parameter, or property that has not been assigned a value, it automatically provides undefined. You do not need to (and should not) assign undefined manually. The language handles it for you.
When to Use null
null is never assigned automatically by JavaScript. It always comes from your own code or from an API that explicitly returns null to signal "no value":
// 1. Explicit empty assignment
let selectedUser = null; // "No user selected yet"
// 2. Clearing a reference
let cachedData = { items: [1, 2, 3] };
cachedData = null; // Explicitly release the reference
// 3. DOM API returns null for missing elements
const element = document.getElementById("nonexistent");
console.log(element); // null
// 4. Regular expression no-match
const match = "hello".match(/xyz/);
console.log(match); // null
// 5. JSON.parse converts "null" to null
const data = JSON.parse('{"value": null}');
console.log(data.value); // null
// 6. Prototype chain end
console.log(Object.getPrototypeOf(Object.prototype)); // nulltypeof: The Biggest Difference
The typeof operator returns completely different results for undefined and null:
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object"typeof null returning "object" is a well-known bug from JavaScript's first implementation in 1995. The original engine stored values with type tags, where objects had a type tag of 0 and null was represented as a null pointer (also 0). This misidentification was never fixed because changing it would break existing websites.
// This means typeof alone cannot distinguish null from objects
const values = [null, {}, [], new Date()];
values.forEach(v => {
console.log(typeof v); // "object" for ALL of them
});
// You must use === null for reliable null detection
function isNull(value) {
return value === null;
}Equality Comparisons
Understanding how undefined and null behave with equality operators is critical:
// Loose equality (==): undefined and null are EQUAL to each other
console.log(undefined == null); // true
console.log(null == undefined); // true
// Strict equality (===): they are NOT equal
console.log(undefined === null); // false
console.log(null === undefined); // false
// Neither equals any other falsy value with ==
console.log(undefined == 0); // false
console.log(undefined == ""); // false
console.log(undefined == false); // false
console.log(null == 0); // false
console.log(null == ""); // false
console.log(null == false); // false| Comparison | Result | Why |
|---|---|---|
undefined == null | true | Special rule: they are loose-equal to each other |
undefined === null | false | Different types: "undefined" vs "object" |
undefined == 0 | false | undefined does not coerce to 0 in == |
null == 0 | false | null does not coerce to 0 in == |
undefined == false | false | undefined only loose-equals null |
null == false | false | null only loose-equals undefined |
The == Shortcut for Both
Because undefined == null is true (and they are not loose-equal to anything else), you can use value == null as a shortcut to check for both undefined and null simultaneously. This is one of the few legitimate uses of loose equality in JavaScript. However, === is still safer for most comparisons.
// The == null shortcut checks for both undefined and null
function processValue(value) {
if (value == null) {
// This catches BOTH undefined and null
console.log("No value provided");
return;
}
console.log("Value:", value);
}
processValue(undefined); // "No value provided"
processValue(null); // "No value provided"
processValue(0); // "Value: 0"
processValue(""); // "Value: "
processValue(false); // "Value: false"Type Coercion Differences
undefined and null behave differently when JavaScript coerces them to other types:
To Number
console.log(Number(undefined)); // NaN
console.log(Number(null)); // 0
// This affects arithmetic
console.log(undefined + 1); // NaN (undefined becomes NaN)
console.log(null + 1); // 1 (null becomes 0)
console.log(undefined * 5); // NaN
console.log(null * 5); // 0To String
console.log(String(undefined)); // "undefined"
console.log(String(null)); // "null"
// Template literals
console.log(`Value: ${undefined}`); // "Value: undefined"
console.log(`Value: ${null}`); // "Value: null"To Boolean
// Both are falsy
console.log(Boolean(undefined)); // false
console.log(Boolean(null)); // false
// Both fail truthy checks
if (!undefined) console.log("undefined is falsy"); // prints
if (!null) console.log("null is falsy"); // prints| Conversion | undefined | null |
|---|---|---|
| To Number | NaN | 0 |
| To String | "undefined" | "null" |
| To Boolean | false | false |
Complete Comparison Table
| Property | undefined | null |
|---|---|---|
| Meaning | No value assigned yet | Intentionally empty |
| Assigned by | JavaScript (automatically) | Developer (explicitly) |
| typeof | "undefined" | "object" (bug) |
| Number coercion | NaN | 0 |
| Boolean coercion | false | false |
== null | true | true |
=== null | false | true |
== undefined | true | true |
=== undefined | true | false |
| In JSON | Omitted from output | Preserved as null |
| Default parameter trigger | Yes | No |
JSON Behavior
undefined and null behave very differently with JSON.stringify() and JSON.parse():
// undefined is OMITTED from JSON objects
const withUndefined = { name: "Alice", age: undefined };
console.log(JSON.stringify(withUndefined)); // '{"name":"Alice"}'
// The age property disappears entirely!
// null is PRESERVED in JSON
const withNull = { name: "Alice", age: null };
console.log(JSON.stringify(withNull)); // '{"name":"Alice","age":null}'
// undefined in arrays becomes null
const arr = [1, undefined, 3];
console.log(JSON.stringify(arr)); // '[1,null,3]'
// JSON does not have an "undefined" value
console.log(JSON.parse("null")); // null
// JSON.parse("undefined") would throw SyntaxErrorThis means if you send data through a JSON round-trip, undefined values silently disappear:
const original = { a: 1, b: undefined, c: null };
const roundTripped = JSON.parse(JSON.stringify(original));
console.log(roundTripped); // { a: 1, c: null }
console.log("b" in roundTripped); // false (b is gone!)
console.log("c" in roundTripped); // true (c survived as null)Default Parameters and undefined
JavaScript default parameters only trigger for undefined, not for null:
function createUser(name = "Anonymous", role = "viewer") {
return { name, role };
}
// undefined triggers the default
console.log(createUser()); // { name: "Anonymous", role: "viewer" }
console.log(createUser(undefined)); // { name: "Anonymous", role: "viewer" }
// null does NOT trigger the default
console.log(createUser(null)); // { name: null, role: "viewer" }
console.log(createUser(null, null)); // { name: null, role: null }This is important when designing APIs: if callers should be able to pass explicit "no value" while still getting a default, they should pass undefined. If you want to accept null as a valid (intentional) value, do not use default parameters for that logic.
Destructuring Behavior
Destructuring defaults also only trigger for undefined:
// Object destructuring
const config = { theme: null, language: undefined };
const { theme = "dark", language = "en" } = config;
console.log(theme); // null (default NOT applied)
console.log(language); // "en" (default WAS applied)
// Array destructuring
const [a = 1, b = 2, c = 3] = [undefined, null];
console.log(a); // 1 (default applied, value was undefined)
console.log(b); // null (default NOT applied, null is a real value)
console.log(c); // 3 (default applied, no element at index 2)Best Practices
Choosing Between undefined and null
Having a consistent convention across your codebase prevents confusion and reduces bugs.
Use null for intentional empty values. When you want to explicitly communicate "no value" or "not yet loaded," assign null. This makes your intent clear to other developers reading the code:
// Clear intent: user data has not loaded yet
let currentUser = null;
// After loading
currentUser = { name: "Alice", email: "alice@example.com" };
// After logout
currentUser = null;Let JavaScript handle undefined naturally. Do not assign undefined to variables yourself. If a variable does not need a value yet, either declare it without assignment or use null to indicate intentional emptiness.
Use strict equality for type-safe checks. Compare with === undefined or === null when you need to distinguish between the two. Use == null only when you genuinely want to catch both.
Handle both in function arguments. When validating function inputs, consider whether null and undefined should be treated the same or differently:
function fetchData(userId) {
if (userId == null) {
throw new Error("userId is required");
}
// This catches both undefined (not passed) and null (explicitly empty)
}Prefer nullish coalescing (??) over logical OR (||) for defaults. The ?? operator only triggers for null and undefined, while || triggers for any falsy value including 0 and empty string:
const port = config.port ?? 3000; // Only uses 3000 if port is null/undefined
const port2 = config.port || 3000; // Also uses 3000 if port is 0 (a bug!)Common Mistakes and How to Avoid Them
Watch Out for These Pitfalls
These mistakes stem from treating undefined and null as interchangeable when they have different behaviors.
Using typeof to check for null. Since typeof null === "object", this check does not work. Use value === null for null detection:
// Wrong: matches null, {}, [], Date, etc.
if (typeof value === "object") { /* unreliable */ }
// Correct
if (value === null) { /* specifically null */ }Forgetting that null + number equals a number. Because Number(null) is 0, arithmetic with null silently produces valid but wrong results:
const items = null;
console.log(items + 5); // 5 (looks correct but items was null!)
console.log(items * 10); // 0 (silent zero, not an error)Expecting default parameters to trigger for null. If you pass null to a function with default parameters, the defaults are skipped because null is a deliberate value, not a missing one.
Using || when you mean ??. The OR operator treats 0, "", and false as "empty" and replaces them with the default. Nullish coalescing only replaces null and undefined:
const count = 0;
console.log(count || 10); // 10 (wrong! 0 is a valid count)
console.log(count ?? 10); // 0 (correct! 0 is not null/undefined)Not accounting for undefined in JSON serialization. Properties with undefined values disappear when serialized to JSON. If you need to preserve "empty" properties, use null instead.
Next Steps
Explore why you should not assign undefined manually
Understanding why assigning undefined manually is considered bad practice will help you write cleaner, more predictable JavaScript code.
Practice with nullish coalescing and optional chaining
Learn how ?? and ?. work together to handle null and undefined safely in deeply nested data structures.
Build a data loader with state management
Create a data fetching utility that uses null for "not loaded," undefined for missing fields, and proper type guards to handle each state correctly.
Review your codebase for null/undefined inconsistencies
Search for places where you assign undefined explicitly or use || where ?? would be more appropriate. Apply the guidelines from this article to improve consistency.
Rune AI
Key Insights
- Different purposes: undefined means "not yet assigned" (automatic); null means "intentionally empty" (explicit)
- typeof difference:
typeof undefinedis "undefined" buttypeof nullis "object" (a historical bug) - Numeric coercion:
Number(undefined)is NaN whileNumber(null)is 0, affecting arithmetic silently - Default parameters: only undefined triggers defaults; null is treated as a provided value
- Use
??over||: nullish coalescing correctly handles 0, "", and false as valid values
Frequently Asked Questions
Are undefined and null the same thing in JavaScript?
Why does undefined == null return true?
Should I use null or undefined for empty values?
How do I check if a value is null or undefined?
Does JSON support undefined?
Conclusion
Understanding the differences between undefined and null is essential for writing predictable JavaScript. Use null when you want to explicitly signal "no value," and let JavaScript assign undefined naturally for uninitialized state. Remember that they differ in typeof results, numeric coercion (NaN vs 0), JSON serialization (omitted vs preserved), and default parameter behavior (triggers vs does not). Using ?? instead of ||, checking with strict equality, and adopting a consistent convention across your codebase eliminates the most common pitfalls.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.