Understanding null and undefined in JavaScript
Introduction
In JavaScript, null and undefined are two special values that are often used to represent the absence of a value. While they might seem similar, they have distinct meanings and use cases. Understanding these differences is crucial for effective programming and debugging.
This article will explain what null and undefined are, how they differ, and provide examples to help you understand their use.
What is null?
nullis a special value that represents "no value" or "nothing." It is explicitly assigned to a variable to indicate that it is intentionally empty or missing a value.
When to Use null
- To explicitly indicate that a variable has no value.
- When you want to initialize a variable with a clear indication that it will be assigned a value later.
Example
let test = null; // `test` is explicitly assigned `null`, meaning it has no value
console.log(test); // Output: nullIn this example, test is initialized with null, indicating that it currently holds no value.
What is undefined?
undefined is a value automatically assigned by JavaScript to variables that have been declared but not initialized. It also appears in situations where a function does not return a value or when you try to access a property that does not exist.
When undefined Occurs
When declaring a variable without initializing it:
let test; // `test` is declared but not assigned a value
console.log(test); // Output: undefinedWhen a function does not return a value:
function myFunction() {
// No return statement
}
console.log(myFunction()); // Output: undefinedWhen accessing a non-existing property of an object:
const person = { name: 'Alice' };
console.log(person.age); // Output: undefinedHere, person.age is undefined because age is not a property of the person object.
Key Differences Between null and undefined
- null:
- Represents the intentional absence of a value.
- Explicitly assigned to a variable.
- Used to indicate that a variable is deliberately empty.
- undefined:
- Represents a variable that has been declared but not yet initialized.
- Automatically assigned by JavaScript in specific situations.
- Used to indicate that something does not exist or has not been defined.
Example Comparison
let value1 = null; // Explicitly set to null
let value2; // Not initialized, hence undefined
console.log(value1); // Output: null
console.log(value2); // Output: undefinedIn this example, value1 is explicitly set to null, while value2 is undefined because it was declared but not assigned a value.
The Famous typeof null === "object" Quirk π
If you run typeof null in JavaScript, it returns "object":
console.log(typeof null); // Output: "object"However, null is NOT an object. It is a primitive value. It has no properties, no prototype, and is stored in the stack memory as a simple primitive.
So why does typeof null return "object"?
The Historical Explanation (The 1995 Type-Tag Bug)
This behavior is a legacy bug from the very first implementation of JavaScript in 1995.
In the original JavaScript engine (written by Brendan Eich in 10 days), values were stored in 32-bit units. To optimize performance, the engine used the lowest bits of these units to store a type tag:
| Type Tag (Binary) | Meaning |
|---|---|
000 | Object (The value is a reference pointing to an object in the heap) |
1 | Int (31-bit signed integer) |
010 | Double (Double precision floating point number) |
100 | String |
110 | Boolean |
How null Was Represented:
In C-like systems, null is represented as the null pointer (all bits are 0, pointing to memory address 0x00000000).
Because all bits of null were 0, the lowest three bits were also 000. When the typeof operator checked the type tag of the value, it read 000 and concluded: "Ah, this is an object!"
This code logic looked something like this in the original engine:
JS_PUBLIC_API(JSType)
JS_TypeOfValue(JSContext *cx, jsval v)
{
if (JSVAL_IS_VOID(v)) { // undefined
return JSTYPE_VOID;
} else if (JSVAL_IS_OBJECT(v)) { // Checked tag 000
obj = JSVAL_TO_OBJECT(v);
if (!obj) // If the pointer itself is NULL
return JSTYPE_OBJECT; // Returns "object" (Bug!)
// ...
}
// ...
}Why Wasn't It Fixed?
A proposal to fix this in ECMAScript (by making typeof null return "null") was drafted and discussed. However, it was rejected because:
- Backward Compatibility: Millions of existing legacy websites depend on
typeof null === "object". Changing it would break conditions likeif (x && typeof x === "object")and crash countless pages across the internet. - "Don't Break the Web": This is a core philosophy of the TC39 committee. Maintaining backward compatibility is prioritized over correcting historical syntax quirks.
Proof: null is Primitive (Stack-based) and NOT an Object
To prove null is not an object:
- No Properties: Objects can store properties.
nullthrows a TypeError if you try to assign properties to it.const myNull = null; // myNull.foo = "bar"; // β TypeError: Cannot set properties of null - No Prototype: All objects in JavaScript inherit from
Object.prototype(except those created withObject.create(null)).nullhas no prototype.Object.getPrototypeOf(null); // β TypeError: Cannot convert undefined or null to object - Stored on the Stack: Unlike objects which store a reference in the stack pointing to a heap allocation,
nullis stored directly on the stack as a fixed, primitive representation of "empty".
Stack (Value) Heap
βββββββββββββββββββββββββ βββββββββββββββ
β myNull: null (0x00) β β (Empty) β
βββββββββββββββββββββββββ βββββββββββββββ
(Directly stored value, not a pointer to any heap object)How to Safely Check for null
Since typeof is unreliable for null, you should check for it using the strict equality operator (===):
const value = null;
// β WRONG: Returns true for objects, arrays, and null
if (typeof value === "object") { ... }
// β
CORRECT: Explicitly checks for null
if (value === null) {
console.log("Value is null");
}
// β
SAFE OBJECT CHECK: Verifies it is an object and not null
if (value !== null && typeof value === "object") {
console.log("Value is a real object");
}Conclusion
Understanding the differences between null and undefined helps in writing clear and bug-free code. While null is used to explicitly represent an absence of value, undefined is the default value assigned to variables that haven't been initialized or when certain conditions aren't met. By using these values appropriately, you can better manage the absence of data and improve the reliability of your JavaScript applications.