Programming Language
JavaScript
null and undefined

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?

  • null is 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: null

In 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: undefined

When a function does not return a value:

function myFunction() {
  // No return statement
}
 
console.log(myFunction()); // Output: undefined

When accessing a non-existing property of an object:

const person = { name: 'Alice' };
 
console.log(person.age); // Output: undefined

Here, 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: undefined

In 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
000Object (The value is a reference pointing to an object in the heap)
1Int (31-bit signed integer)
010Double (Double precision floating point number)
100String
110Boolean

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:

  1. Backward Compatibility: Millions of existing legacy websites depend on typeof null === "object". Changing it would break conditions like if (x && typeof x === "object") and crash countless pages across the internet.
  2. "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:

  1. No Properties: Objects can store properties. null throws a TypeError if you try to assign properties to it.
    const myNull = null;
    // myNull.foo = "bar"; // ❌ TypeError: Cannot set properties of null
  2. No Prototype: All objects in JavaScript inherit from Object.prototype (except those created with Object.create(null)). null has no prototype.
    Object.getPrototypeOf(null); // ❌ TypeError: Cannot convert undefined or null to object
  3. Stored on the Stack: Unlike objects which store a reference in the stack pointing to a heap allocation, null is 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.