DEV Community

Cover image for The History of undefined and null in JavaScript and their Common Pitfalls
CodeScythe
CodeScythe

Posted on

The History of undefined and null in JavaScript and their Common Pitfalls

Introduction

JavaScript's dual "nothing" values—undefined and null—represent one of the language's most confusing design decisions. This document explores the historical context, technical differences, and practical implications of having two distinct "empty" values in JavaScript.


The Design "Accident"

(Book:- Eloquent JavaScript)

"The difference in meaning between undefined and null is an accident of JavaScript's design, and it doesn't matter most of the time. In cases where you actually have to concern yourself with these values..."


Historical Context: The 10-Day Language

Brendan Eich's Challenge (1995)

When Brendan Eich created JavaScript in May 1995, he had only 10 days to design the first version for Netscape Navigator. Under this extreme time pressure, he borrowed concepts from multiple programming languages:

Language Contribution to JavaScript
Java Syntax, null concept
Scheme Functions as first-class objects, undefined concept
Self Prototype-based inheritance

The Collision of Concepts

From Java: null

// Java approach - single "nothing" value
String name = null; // Intentional absence of value
Enter fullscreen mode Exit fullscreen mode

From Scheme: undefined

; Scheme approach - unbound variables
(define x) ; x exists but has no value
Enter fullscreen mode Exit fullscreen mode

Eich wanted both behaviors, leading to JavaScript's unique dual system.


Technical Differences

Core Definitions

Value Type Meaning Assignment
undefined "undefined" Variable declared but not assigned Automatic
null "object" Intentional absence of value Manual

Automatic vs. Intentional Assignment

// undefined - automatic assignment
let x;
console.log(x); // undefined

function test() {
  // no return statement
}
console.log(test()); // undefined
Enter fullscreen mode Exit fullscreen mode

When the Difference Doesn't Matter

Truthiness Checks

let value1 = undefined;
let value2 = null;

// Both behave the same in boolean contexts
if (!value1) console.log("No value1"); // executes
if (!value2) console.log("No value2"); // executes

// Loose equality
console.log(undefined == null); // true

// Tight equality
console.log(undefined === null); // false
Enter fullscreen mode Exit fullscreen mode

Common Patterns

// Checking for "no value" - works for both
function processData(input) {
  if (input == null) { // catches both undefined and null
    return "No data provided";
  }
  return input.toUpperCase();
}
Enter fullscreen mode Exit fullscreen mode

When the Difference DOES Matter

1. Default Parameters

function greet(name = "Guest") {
  return `Hello, ${name}!`;
}

console.log(greet(undefined)); // "Hello, Guest!"
console.log(greet(null));      // "Hello, null!"
Enter fullscreen mode Exit fullscreen mode

2. JSON Serialization

const data = {
  name: "John",
  age: undefined,
  city: null
};

console.log(JSON.stringify(data));
// Output: {"name":"John","city":null}
// Note: undefined properties are omitted!
Enter fullscreen mode Exit fullscreen mode

3. Strict Equality

console.log(undefined === null);        // false
console.log(typeof undefined);          // "undefined"
console.log(typeof null);              // "object" (historical bug!)
Enter fullscreen mode Exit fullscreen mode

Why typeof null returns "object"

Early JavaScript internals (1995)

When JavaScript was first implemented in Netscape Navigator, values were stored as binary tags — small pieces of metadata used to identify their type.

Each value consisted of:

  • a type tag (in the lower bits)
  • and the actual data (in the remaining bits)

For example, something like this (simplified):

Type Binary Tag
Number 000
String 010
Object 000 (pointer)
Null also 0x00 (null pointer)

So, both objects and null were represented with the same low-level tag:

“Pointer to an object (but if it’s zero, it’s null).”

As a result, when the original typeof operator checked the tag to determine the type, it saw 0 and concluded:

“That’s an object!”

That’s why:

typeof null; // "object"
Enter fullscreen mode Exit fullscreen mode

It’s officially a mistake

Even Brendan Eich (the creator of JavaScript) has called this a bug — but it’s a permanent one, because fixing it would break too much existing code.

This behavior has been preserved for backward compatibility since the 1990s. Changing it now would mean older websites relying on typeof null === "object" might break.


ECMAScript specification confirms it

From the ECMAScript spec:

“If Type(x) is Null, return "object".”

The spec literally defines it this way — not because it’s correct, but because it must maintain compatibility with legacy behavior.


Practical consequence

Since typeof null returns "object", the proper way to test for null is not using typeof.
Instead, use strict equality:

value === null; // ✅ correct way to check for null
Enter fullscreen mode Exit fullscreen mode

If you use typeof, you can’t distinguish null from an object:

typeof {};     // "object"
typeof null;   // "object"  ← indistinguishable
Enter fullscreen mode Exit fullscreen mode

Summary

Concept Explanation
What happened? null was given the same internal tag as objects in the first JavaScript engine.
Why “bug”? It’s an implementation mistake that was never fixed.
Why not fixed? Changing it would break too much existing web code.
How to check for null correctly? Use value === null instead of typeof.

Object Property Access

const obj = { a: null };

console.log(obj.a);        // null (property exists, value is null)
console.log(obj.b);        // undefined (property doesn't exist)
console.log('a' in obj);   // true
console.log('b' in obj);   // false
Enter fullscreen mode Exit fullscreen mode

Historical Quirks and Bugs

The typeof null Bug

typeof null; // "object" - This is a bug that can't be fixed!
Enter fullscreen mode Exit fullscreen mode

This bug exists because in JavaScript's original implementation, values were stored with type tags. null was represented as a null pointer (0x00), which had the same type tag as objects.

Evolution of undefined

  • Early JavaScript: undefined wasn't even a reserved word
  • ES3 (1999): undefined became a global property
  • ES5 (2009): undefined became read-only in strict mode
  • Modern JavaScript: undefined is a primitive value

Best Practices and Modern Usage

Recommended Patterns

// Use null for intentional "empty" values
let currentUser = null; // "no user logged in"

// Let JavaScript handle undefined automatically
let config; // undefined until assigned

// Check for both with loose equality
if (value == null) {
  // handles both undefined and null
}

// Or use nullish coalescing (ES2020)
const result = value ?? "default";
Enter fullscreen mode Exit fullscreen mode

API Design Considerations

// Good: Consistent use of null for API responses
function fetchUser(id) {
  return users.find(u => u.id === id) || null;
}

// Avoid: Mixing undefined and null inconsistently
function badFetch(id) {
  const user = users.find(u => u.id === id);
  return user ? user : null; // Inconsistent with find's undefined
}
Enter fullscreen mode Exit fullscreen mode

Framework and Library Perspectives

Different Approaches

  • JSON: Only supports null, not undefined
  • React: Uses null to represent "no component"
  • Node.js: APIs often return null for "not found"
  • TypeScript: Distinguishes between the two in type checking

Database Interactions

// SQL NULL maps to JavaScript null
const user = await db.query('SELECT name FROM users WHERE id = ?', [123]);
console.log(user.name); // null if SQL NULL, never undefined
Enter fullscreen mode Exit fullscreen mode

Educational Examples

Demonstrating the Difference

// Example 1: Function parameters
function example(required, optional) {
  console.log('required:', required); // undefined if not passed
  console.log('optional:', optional); // undefined if not passed
}

example(); // both undefined

// Example 2: Object destructuring
const { name, age } = {}; // both undefined
const data = { name: null, age: null }; // both null

// Example 3: Array methods
const arr = [1, , 3]; // sparse array
console.log(arr[1]); // undefined (hole in array)

const arr2 = [1, null, 3];
console.log(arr2[1]); // null (explicit value)
Enter fullscreen mode Exit fullscreen mode

Memory and Performance Implications

Internal Representation

// Both are primitive values, but handled differently internally
console.log(undefined === undefined); // true (same reference)
console.log(null === null);           // true (same reference)

// Memory usage is minimal for both
const million_undefineds = new Array(1000000); // sparse, efficient
const million_nulls = new Array(1000000).fill(null); // dense array
Enter fullscreen mode Exit fullscreen mode

Conclusion

The coexistence of undefined and null in JavaScript represents a fascinating case study in language design under pressure. While their distinction often doesn't matter in day-to-day programming, understanding their origins and differences helps developers:

  1. Write more predictable code by understanding when each appears
  2. Debug more effectively by recognizing the subtle differences
  3. Design better APIs by choosing consistent patterns
  4. Appreciate language evolution and the importance of careful design

Key Takeaways

  • Historical Context: The dual system emerged from mixing Java and Scheme concepts under time pressure
  • Practical Impact: Most of the time, treat them as equivalent "no value" indicators
  • Critical Cases: JSON serialization, default parameters, and strict equality checks require awareness of the difference
  • Modern Approach: Use null for intentional emptiness, let JavaScript handle undefined automatically

Further Reading


"JavaScript's undefined and null remind us that even in software design, historical accidents can become permanent features that millions of developers must understand and work with daily."

Top comments (0)