null vs undefined — 'Cannot read property of null' Crash
Production outage: user.profile.name crashed on null in production.
20+ years shipping production JavaScript and front-end systems at scale. Lessons pulled from things that broke in production.
- ✓Basic programming fundamentals
- ✓A computer with internet access
- ✓Willingness to follow along with examples
- null and undefined both represent 'no value' but come from different sources.
- undefined is JavaScript's automatic default for uninitialized variables, missing properties, and absent returns.
- null is a programmer-assigned signal: 'I explicitly set this to nothing'.
- Performance: nullish coalescing (??) is safer than || when 0, '', or false are valid values.
- Production insight: Default function parameters only catch undefined, not null — leading to silent 'null' strings in output.
- Biggest mistake: typeof null returns 'object' — never use typeof to check for null.
Imagine you order a package online. 'undefined' is like checking your doorstep before the delivery truck has even left the warehouse — the package doesn't exist in your world yet, nobody told you anything about it. 'null' is like a delivery driver showing up and handing you an empty box on purpose — someone made a deliberate decision that nothing goes here. Both mean 'no value', but one is an accident of timing and the other is an intentional choice.
| Chrome | Firefox | Safari | Edge |
|---|---|---|---|
| ✓ | ✓ | ✓ | ✓ |
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.
userProfile.address.street but userProfile.address was undefined — JavaScript couldn't go one level deeper. Always check the outer value before drilling into a nested property.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.
JSON Serialization: undefined is Omitted, null is Preserved
One of the most practical differences between null and undefined appears when you serialize JavaScript objects to JSON. The JSON.stringify() method treats them very differently: properties with a value of undefined are removed from the output entirely, while properties with null are included with the literal JSON null.
This distinction is critical for API design. If your backend returns an object with an optional field that is undefined, that field simply won't appear in the JSON response. But if the same field is null, it will appear as "fieldName": null. Client code that expects the field to always exist will crash if it gets undefined, but can safely check for null.
For this reason, many teams standardize on using null for all 'no value' cases in API responses. Omitting fields entirely can lead to inconsistencies and bugs when clients iterate over keys or check for property existence. Using null ensures the shape of the response is predictable.
When consuming an API, you should always anticipate that a field might be missing (undefined) or explicitly null. Use optional chaining and nullish coalescing to handle both safely.
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.
typeof null returns 'object' — not 'null'. This is a 25-year-old bug in JavaScript that was never fixed because fixing it would break the internet. To safely check for null, always use value === null, never typeof value === 'null' (that will never be true).typeof x === 'object' && x !== null, someone is working around the bug.undefined vs null: 8-Dimension Comparison Table
Here's a side-by-side comparison of undefined and null across eight key dimensions that every developer should know:
| Dimension | undefined | null |
|---|---|---|
| Declaration | Default for uninitialized variables | Must be explicitly assigned |
| Assignment | JavaScript assigns automatically | Programmer assigns deliberately |
| typeof | 'undefined' | 'object' (historical bug) |
| JSON.stringify | Omitted from output | Included as null |
| Loose equality (==) | undefined == null → true | null == undefined → true |
| Strict equality (===) | undefined === null → false | null === undefined → false |
| Purpose | Accidental 'no value' from system | Intentional 'no value' by developer |
| Data type | undefined (primitive) | null (primitive, but typeof lies) |
This table should be your quick reference whenever you're debugging null/undefined confusion. Memorize the typeof quirk — it's a common interview pitfall.
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.
createEmailGreeting(null), the default 'Valued Customer' does NOT kick in. You get the string 'null' rendered into your template. If null is a realistic input, add an explicit guard inside the function body: recipientName = recipientName ?? 'Valued Customer'.Nullish Coalescing (??) Cannot Mix with || or && Without Parentheses
When you start combining nullish coalescing with logical OR or logical AND, JavaScript enforces a strict rule: you cannot write something like a ?? b || c or a && b ?? c without parentheses. The language specification explicitly forbids mixing ?? with || or && unless you explicitly wrap one of the operators with parentheses.
This is because the precedence of ?? is deliberately vague next to || and &&. The JavaScript committee (TC39) decided that forcing developers to be explicit about intent would prevent subtle bugs. If you try to write a ?? b || c, you'll get a SyntaxError: "Cannot mix '??' and '||' without parentheses". The same applies for a && b ?? c.
To work around this, always add parentheses. For example: (a ?? b) || c or a ?? (b || c). The parentheses clarify which operation happens first and make your code easier to read and reason about.
This limitation often catches developers by surprise, especially those accustomed to freely chaining || and &&. It's a conscious design choice to improve code clarity.
no-mixed-operators rule will catch this during development.Falsy Values: When ?? vs || Matters
A common source of bugs is assuming || (logical OR) and ?? (nullish coalescing) behave the same way. They don't. The || operator returns the right-hand side if the left-hand side is any falsy value: false, 0, '', null, undefined, NaN. In contrast, ?? only triggers on null or undefined.
This difference is critical when working with numbers that can legitimately be 0, strings that can be empty, booleans that can be false, or NaN. Using || in these cases will incorrectly replace valid falsy values with defaults, causing silent data corruption.
Here's a comparison table of how each operator behaves with these falsy values:
| Value | `value | 'default'` | value ?? 'default' | |
|---|---|---|---|---|
null | 'default' | 'default' | ||
undefined | 'default' | 'default' | ||
0 | 'default' | 0 (preserved) | ||
'' | 'default' | '' (preserved) | ||
false | 'default' | false (preserved) | ||
NaN | 'default' | NaN (preserved) |
Use || when you want to treat all falsy values as absent (e.g., a count that might be 0 should never be 0 — but that's rare). Use ?? when only semantic emptiness matters. In practice, most real-world data (scores, counts, empty strings) are legitimate values, so ?? is the safer default choice.
prefer-nullish-coalescing) to enforce ?? over || in fallback expressions.Real-World Production Scenarios: Debugging and Preventing null/undefined Errors
In production, null/undefined bugs often surface in three repeatable scenarios: API response shape changes, missing data for edge cases, and incorrect default value handling.
Scenario 1: A third-party API adds a new optional field but returns null instead of omitting it. Your code uses || for fallback and accidentally replaces legitimate empty strings.
Scenario 2: A user completes an action that calls a callback, but the callback was never passed in — it's undefined. Calling it throws a TypeError that isn't caught.
Scenario 3: A migration script fails to set a default value for existing records, leaving a field as undefined. The frontend expects that field to always exist and crashes.
The common thread: assuming a value will always be present. Defensive coding is not paranoia — it's the difference between a 99.9% uptime and a P1 incident at 3 AM.
- A null value in a chain poisons all subsequent property accesses.
- Optional chaining stops the poison but silently returns undefined — now you have undefined instead of null.
- Always handle the result of optional chaining with a default or a check.
- Think of null as a deliberate 'stop' sign. Undefined is an accidental 'stop'.
null values for every optional field.5 Practice Exercises to Master null/undefined Handling
These exercises simulate real-world scenarios you'll encounter on the job. Try to solve each one before looking at the solution. Focus on safe property access, API response handling, and default parameter patterns.
The typeof Operator Lies About null — Here's Why That Matters
You've probably seen it: typeof null === "object". This is a bug from JavaScript's first implementation that we're stuck with forever. It's not intentional. It's not clever. It's a historical accident. But you must know it because it breaks type guards. If you write if (typeof value === "object") thinking you're checking for a real object, null sneaks in. I've seen production apps crash because a null — assumed impossible — slipped past a type guard and called null.someMethod(). The fix: always check for null explicitly when using typeof "object". The typeof operator on undefined returns "undefined", which is correct. So you get one reliable falsy type check and one broken one. Remember which is which.
value !== null before accessing properties.Falsy Values Are Not Optional: Why Default Parameters Behave Differently
JavaScript default parameters only kick in for undefined, not for null or other falsy values. This catches everyone at least once. You write function greet(name = "User") expecting it to handle any falsy input. But when someone passes null, 0, or "", your default is ignored. The parameter gets the falsy value instead. This is by design: default parameters were created to handle the "missing argument" case — which produces undefined — not to sanitize bad input. If you want to handle all falsy values, you need manual checks or the nullish coalescing operator (??). The || operator defaults on all falsy values, which is often too aggressive. The ?? operator defaults only on null/undefined. Choose the tool that matches your intent: ?? for absence, || for falsy replacement.
JSON Serialization Silently Deletes undefined — And That Breaks APIs
Here's a common production bug: you build an API response object, set some fields to undefined intentionally to omit them in the JSON, then wonder why your schema validation fails. The truth is more dangerous. JSON.stringify() silently removes all properties with undefined values. No error. No warning. Your object shape changes. Arrays with undefined elements become null instead. This is different from null, which serializes as "key": null — preserving the property and its intent. I've seen teams debug for hours because an optional field was missing from a response, breaking strict schema validation on the client. The fix: if you want a property to appear in JSON, use null. If you want it omitted, use undefined or delete. But be explicit. Default array initialization with Array(3) produces undefined slots — always fill() or map them to null before serialization.
?.) and get undefined, serialization silently drops that key. Always assume JSON.stringify swallows undefined — validate your output before sending to clients.The 'Cannot Read Property of Undefined' Production Outage
user: { profile: null } for a newly registered user who hadn't completed their profile. The frontend code used user.profile.name directly, causing a crash on null.user?.profile?.name ?? 'Guest'. Also enforced a rule: never access nested properties without checking the parent first.- Every nested property access is a potential crash point — treat null like a landmine.
- Use optional chaining and nullish coalescing as default patterns, not afterthoughts.
- Standardize API responses: missing data should be null, not undefined, so consumers can use a single guard pattern.
a.b.c). Log the parent object with console.log before the crash. Add optional chaining at the first likely null/undefined link.const { name } = user ?? {}. Or guard before destructuring.console.log(parentObj, 'parent just before crash');JSON.parse(JSON.stringify(parentObj)) to see the structure (or use structured clone).parentObj?.x and a fallback with ??.| File | Command / Code | Purpose |
|---|---|---|
| WhenJavaScriptCreatesUndefined.js | let userAge; // JavaScript quietly sets this to undefined | What 'undefined' Actually Means |
| WhenToUseNull.js | let currentUser = null; | What 'null' Actually Means |
| JSONSerializationBehavior.js | const user = { | JSON Serialization |
| EqualityCheckExplained.js | console.log('--- Loose Equality (==) ---'); | Equality Checks |
| ComparisonQuickRef.js | console.log(typeof undefined); // 'undefined' | undefined vs null |
| ModernNullSafePatterns.js | const loggedInUser = { | Practical Patterns |
| NullishCoalescingChaining.js | const option1 = (null ?? 'fallback') || 'default'; | Nullish Coalescing (??) Cannot Mix with || or && Without Par |
| FalsyOperatorComparison.js | const testValues = [null, undefined, 0, '', false, NaN]; | Falsy Values |
| ProductionDebugPatterns.js | const apiResponse = { | Real-World Production Scenarios |
| PracticeExercises.js | const apiResponse = { user: { address: null } }; // or could be { user: null } | 5 Practice Exercises to Master null/undefined Handling |
| typeof-trap.js | console.log(typeof null); // "object" — THIS IS THE BUG | The typeof Operator Lies About null |
| default-params.js | function greet(name = "User") { | Falsy Values Are Not Optional |
| json-serialization.js | const payload = { | JSON Serialization Silently Deletes undefined |
Key takeaways
typeof null returns 'object'=== null to check for null, never typeof.Interview Questions on This Topic
What 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?
currentUser = null. That's a clear, intentional empty state. If you left it undefined, you couldn't distinguish between 'no one has logged in yet' and 'the variable has not been initialized'. Using null makes your intent explicit and easier to reason about.Frequently Asked Questions
20+ years shipping production JavaScript and front-end systems at scale. Lessons pulled from things that broke in production.
That's JS Basics. Mark it forged?
8 min read · try the examples if you haven't