null vs undefined in JavaScript — What They Mean and When to Use Each
Every JavaScript program you'll ever write will eventually bump into null and undefined. They look similar on the surface — both seem to mean 'nothing' — but treating them as the same thing causes some of the most mysterious bugs you'll encounter as a developer. A form field that should be blank, a user profile that hasn't loaded yet, a database record that genuinely has no value — all of these map to slightly different meanings in code, and JavaScript gives you two distinct tools to express them.
The problem is that JavaScript itself uses undefined automatically behind the scenes, and developers often reach for null without fully understanding when that's the right call. Mixing them up leads to buggy equality checks, confusing API responses, and code that works 90% of the time and fails in ways that are genuinely hard to debug.
By the end of this article you'll know exactly what each value represents, where JavaScript creates undefined without you asking it to, how to write equality checks that don't lie to you, and when to intentionally use null in your own code. You'll also have the answers to the interview questions that trip up candidates who only half-understand this topic.
What 'undefined' Actually Means — and Who Creates It
The key insight about undefined is this: you almost never write it yourself. JavaScript creates it automatically whenever you try to access something that hasn't been given a value yet.
Declare a variable without assigning it? JavaScript sets it to undefined. Call a function that doesn't explicitly return anything? It returns undefined. Access an object property that doesn't exist? You get undefined back. Ask for the 10th item in a 3-item array? undefined.
Think of undefined as JavaScript's way of shrugging and saying 'I have no information about this.' It's the system's default — a placeholder that means 'this slot exists, but nobody put anything in it yet.'
This is why undefined is generally not something you should assign deliberately in your own code. If you write let userName = undefined, you're basically doing JavaScript's job for it in a confusing way. When you want to express 'no value', that's what null is for — which we'll get to next.
Understanding where JavaScript produces undefined on its own is crucial because it's the source of many 'cannot read properties of undefined' errors that beginners dread.
// ── CASE 1: Variable declared but never assigned ────────────────────────── let userAge; // JavaScript quietly sets this to undefined console.log('Declared but unassigned:', userAge); // undefined // ── CASE 2: Accessing an object property that does not exist ────────────── const userProfile = { firstName: 'Maria', lastName: 'Santos' }; // We never defined an email property — JavaScript returns undefined for it console.log('Missing property:', userProfile.email); // undefined // ── CASE 3: A function with no return statement ─────────────────────────── function greetUser(name) { // This function does work but forgets to return a value const message = `Hello, ${name}!`; // <-- no return statement here } const greeting = greetUser('Carlos'); console.log('No-return function result:', greeting); // undefined // ── CASE 4: Accessing an array index that doesn't exist ─────────────────── const colors = ['red', 'green', 'blue']; // indexes 0, 1, 2 exist console.log('Out-of-bounds index:', colors[10]); // undefined // ── CASE 5: A function parameter that was never passed ──────────────────── function displayScore(playerName, score) { // If score is not passed, JavaScript sets it to undefined automatically console.log(`${playerName} scored: ${score}`); } displayScore('Alex'); // score is never passed in
Missing property: undefined
No-return function result: undefined
Out-of-bounds index: undefined
Alex scored: undefined
What 'null' Actually Means — and When YOU Should Use It
null is a deliberate, programmer-assigned value. It means: 'I know this variable exists, I know it should hold a value eventually, and right now I am explicitly saying there is nothing here.'
This is a crucial distinction. undefined is what JavaScript gives you when it doesn't know. null is what you give JavaScript when you do know — specifically, when you know the answer is 'nothing'.
A real-world example: imagine you're building a user profile page. Before the user logs in, currentUser should be null — you're not waiting for data to load, you're consciously saying 'there is no logged-in user right now.' The moment they log in, you set currentUser to their actual data. When they log out, you set it back to null.
Another example: a database might store a user's middle name field as null because the user doesn't have a middle name. That's different from the field never being sent in the API response at all (which would likely give you undefined).
null is also the value you'll set variables to when you want to release memory — setting a large object to null tells the JavaScript engine's garbage collector that the memory can be reclaimed.
The short rule: you write null, JavaScript writes undefined.
// ── SCENARIO: A user authentication system ─────────────────────────────── // Before login: we deliberately set currentUser to null. // This means 'no user is logged in right now' — an intentional empty state. let currentUser = null; console.log('Before login, currentUser:', currentUser); // null // A function that simulates fetching a user from a database function findUserById(userId) { const database = [ { id: 1, name: 'Priya Nair', role: 'admin' }, { id: 2, name: 'Jake Thompson', role: 'viewer' } ]; const foundUser = database.find(user => user.id === userId); // If the user does not exist in the database, we return null deliberately. // This tells the caller: 'I looked — there is genuinely nobody here.' return foundUser || null; } // User 1 exists currentUser = findUserById(1); console.log('After finding user 1:', currentUser); // { id: 1, name: 'Priya Nair', role: 'admin' } // User 99 does not exist — we get null back intentionally const missingUser = findUserById(99); console.log('Looking for user 99:', missingUser); // null // ── Checking for null in a real condition ───────────────────────────────── if (missingUser === null) { console.log('No user found — show a 404 page or an error message.'); } // ── Setting to null on logout ───────────────────────────────────────────── function logout() { currentUser = null; // Deliberately clearing the value on logout console.log('User logged out. currentUser is now:', currentUser); } logout();
After finding user 1: { id: 1, name: 'Priya Nair', role: 'admin' }
Looking for user 99: null
No user found — show a 404 page or an error message.
User logged out. currentUser is now: null
Equality Checks — The Trap That Catches Every Beginner
Here's where things get genuinely dangerous if you're not careful. JavaScript has two equality operators: == (loose equality) and === (strict equality). With null and undefined, the difference between them is critical.
Loose equality (==) considers null and undefined to be equal to each other — and only to each other. So null == undefined is true. But null == 0 is false, and null == false is false. This is one of JavaScript's infamous quirks.
Strict equality (===) checks both value AND type. Since null and undefined are different types, null === undefined is false. This is almost always what you want.
In practice, there's one genuinely useful situation for the loose check: if you want to test whether a value is either null OR undefined — and you don't care which — you can write value == null. This is actually a common pattern in professional code, because API responses sometimes give you null and sometimes give you nothing at all (undefined).
For everything else, use ===. Predictability is more valuable than brevity.
// ── LOOSE EQUALITY (==) — Full of surprises ─────────────────────────────── console.log('--- Loose Equality (==) ---'); console.log(null == undefined); // true — they ARE loosely equal console.log(null == 0); // false — null does NOT loosely equal zero console.log(null == false); // false — null does NOT loosely equal false console.log(null == ''); // false — null does NOT loosely equal empty string console.log(undefined == false); // false — undefined does NOT loosely equal false // ── STRICT EQUALITY (===) — No surprises ───────────────────────────────── console.log('\n--- Strict Equality (===) ---'); console.log(null === undefined); // false — different types, different values console.log(null === null); // true — null is strictly equal to itself console.log(undefined === undefined); // true — same deal // ── THE ONE GOOD USE OF LOOSE NULL CHECK ────────────────────────────────── // This pattern checks: 'is this value either null OR undefined?' // Very useful when an API might return one or the other. console.log('\n--- Practical Null Check Pattern ---'); function processApiResponse(responseValue) { // This single check catches both null and undefined safely if (responseValue == null) { console.log('No value present (was null or undefined) — using default.'); return 'Default display text'; } return responseValue; } console.log(processApiResponse(null)); // null triggers the guard console.log(processApiResponse(undefined)); // undefined triggers the guard too console.log(processApiResponse('Hello!')); // real value passes through // ── typeof REVEALS THE DIFFERENCE ──────────────────────────────────────── // This is a famous JavaScript quirk worth knowing console.log('\n--- typeof Results ---'); console.log(typeof null); // 'object' <-- historical bug in JavaScript! console.log(typeof undefined); // 'undefined'
true
false
false
false
false
--- Strict Equality (===) ---
false
true
true
--- Practical Null Check Pattern ---
No value present (was null or undefined) — using default.
No value present (was null or undefined) — using default.
Hello!
--- typeof Results ---
object
undefined
Practical Patterns — Writing Code That Handles Both Gracefully
Knowing the theory is half the battle. The other half is writing code that handles null and undefined without crashing.
Modern JavaScript gives you several elegant tools for this. The optional chaining operator (?.) lets you safely drill into nested objects without throwing an error if something in the chain is null or undefined. The nullish coalescing operator (??) lets you provide a fallback value specifically when something is null or undefined — unlike the OR operator (||) which also replaces falsy values like 0 and empty strings, which can cause bugs.
Default function parameters handle the case where a caller doesn't pass an argument (undefined), but won't kick in for null — which is sometimes exactly what you want, and sometimes not, so it's worth being aware of the distinction.
These tools exist specifically because dealing with missing values is so common in real JavaScript. API calls fail, form fields are left empty, database records have optional columns. Professional code is full of these defensive patterns — not because developers are paranoid, but because data in the real world is messy.
// ── SCENARIO: Working with a user profile from an API ──────────────────── const loggedInUser = { name: 'Sofia Reyes', address: { city: 'Barcelona' // Note: no 'zipCode' property — it's missing entirely }, subscriptionLevel: null // User exists but has no active subscription (intentional null) }; const guestUser = null; // No user is logged in (intentional null) // ── OPTIONAL CHAINING (?.) — Safe property access ──────────────────────── // Without ?., accessing guestUser.name would throw a TypeError and crash your app. // With ?., if guestUser is null/undefined, it short-circuits and returns undefined safely. console.log('Guest city:', guestUser?.address?.city); // undefined (no crash!) console.log('User city:', loggedInUser?.address?.city); // 'Barcelona' console.log('User zip:', loggedInUser?.address?.zipCode); // undefined (property missing) // ── NULLISH COALESCING (??) — Safe fallback values ──────────────────────── // ?? only kicks in when the value is null OR undefined. // Unlike ||, it does NOT replace 0, false, or empty string. const displayCity = loggedInUser?.address?.city ?? 'City not provided'; console.log('Display city:', displayCity); // 'Barcelona' const displayZip = loggedInUser?.address?.zipCode ?? 'ZIP not provided'; console.log('Display ZIP:', displayZip); // 'ZIP not provided' const subscriptionLabel = loggedInUser.subscriptionLevel ?? 'Free tier'; console.log('Subscription:', subscriptionLabel); // 'Free tier' (because value was null) // ── WHY ?? IS SAFER THAN || FOR NUMBERS ─────────────────────────────────── const playerScore = 0; // A legitimate score of zero // BUG: || treats 0 as falsy and replaces it — wrong! const scoreWithOR = playerScore || 'No score yet'; console.log('Score with ||:', scoreWithOR); // 'No score yet' <-- WRONG! 0 is a real score // CORRECT: ?? only replaces null/undefined, leaves 0 alone const scoreWithNullish = playerScore ?? 'No score yet'; console.log('Score with ??:', scoreWithNullish); // 0 <-- Correct! // ── DEFAULT PARAMETERS — Handle undefined, NOT null ────────────────────── function createEmailGreeting(recipientName = 'Valued Customer') { // Default only fires when recipientName is undefined, not when it's null return `Dear ${recipientName}, thank you for your order!`; } console.log(createEmailGreeting('James')); // Default not used — real name provided console.log(createEmailGreeting()); // Default used — no argument passed (undefined) console.log(createEmailGreeting(null)); // Default NOT used — null is explicit!
User city: Barcelona
User zip: undefined
Display city: Barcelona
Display ZIP: ZIP not provided
Subscription: Free tier
Score with ||: No score yet
Score with ??: 0
Dear James, thank you for your order!
Dear Valued Customer, thank you for your order!
Dear null, thank you for your order!
| Feature / Aspect | undefined | null |
|---|---|---|
| Who creates it? | JavaScript creates it automatically | You create it deliberately in your code |
| What it means | 'This slot exists but was never filled' | 'I intentionally set this to empty' |
| typeof result | 'undefined' | 'object' (historical JS bug — don't rely on this) |
| Strict equality (===) | undefined === undefined → true | null === null → true |
| Loose equality with each other | null == undefined → true | null == undefined → true |
| Default function params trigger? | Yes — undefined triggers defaults | No — null does NOT trigger defaults |
| Optional chaining (?.) triggers? | Yes — stops and returns undefined | Yes — stops and returns undefined |
| Nullish coalescing (??) triggers? | Yes — fallback value is used | Yes — fallback value is used |
| JSON.stringify behavior | Properties with undefined are omitted | Properties with null are included as null |
| When to use it in your own code | Almost never — let JavaScript handle it | When you want to explicitly signal 'no value' |
🎯 Key Takeaways
- undefined is JavaScript's default — it appears automatically when a variable is declared but not assigned, a property doesn't exist, or a function has no return statement. You almost never write it yourself.
- null is a deliberate programmer signal meaning 'I know this slot exists, and right now it contains nothing' — use it when you want to express an intentional empty state, like a logged-out user or a missing database record.
typeof nullreturns 'object' — this is a 25-year-old JavaScript bug. Always use=== nullto check for null, never typeof.- Use the nullish coalescing operator (??) instead of || for fallback values when 0, false, or an empty string are valid real values in your data — ?? only triggers on null and undefined, while || triggers on all falsy values.
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Using === to check for 'either null or undefined' separately — e.g. writing
if (value === null || value === undefined)repeatedly everywhere. This works, but it's verbose. Fix: useif (value == null)— this single loose check safely catches both, and is an accepted professional pattern precisely because null and undefined are the only things that are loosely equal to each other. - ✕Mistake 2: Using || instead of ?? for null/undefined fallbacks when the real value could be falsy — e.g.
const count = apiResponse.itemCount || 0will replace a legitimate count of 0 with 0 — fine here, butconst label = product.badge || 'No badge'will replace an empty string '' with 'No badge' even if the API explicitly sent an empty string. Fix: use ?? instead of || whenever your fallback logic should only care about null/undefined, not all falsy values. - ✕Mistake 3: Checking for null with typeof — e.g.
if (typeof userValue === 'null'). This will never be true because typeof null returns the string 'object', not 'null'. This is a famous JavaScript bug from 1995 that was never patched. Fix: always check for null with strict equality:if (userValue === null). Similarly, never use typeof to distinguish null from an actual object — use === null for null checks.
Interview Questions on This Topic
- QWhat is the difference between null and undefined in JavaScript, and can you give a real-world scenario where you'd choose to use null over just leaving a variable unassigned?
- QWhy does `typeof null` return 'object' in JavaScript, and how would you correctly check if a value is null at runtime?
- QWhat is the difference between the || (OR) operator and the ?? (nullish coalescing) operator for providing fallback values — and can you give an example where || produces a bug that ?? would prevent?
Frequently Asked Questions
Is null equal to undefined in JavaScript?
With loose equality (==), yes — null == undefined evaluates to true. But with strict equality (===), no — null === undefined is false because they are different types. In professional code, use strict equality (===) unless you're intentionally writing a guard that catches both at once with value == null.
Should I ever assign undefined to a variable myself?
Almost never. JavaScript assigns undefined automatically for uninitialized variables, missing properties, and missing function arguments. If you want to express 'this has no value intentionally', use null instead. Assigning undefined yourself makes your code confusing because readers can't tell if it was deliberate or an oversight.
Why does a function return undefined when I forget the return statement?
Every JavaScript function must return something. If you don't provide a return statement — or write a bare return with no value — JavaScript automatically returns undefined as the result. This is JavaScript filling in the gap. If your function is supposed to produce a value, double-check you're actually returning it, especially inside if-blocks or loops where it's easy to miss.
Written and reviewed by senior developers with real-world experience across enterprise, startup and open-source projects. Every article on TheCodeForge is written to be clear, accurate and genuinely useful — not just SEO filler.