Why is typeof null === 'object' in JavaScript?

Why is typeof null === 'object' in JavaScript?
Photo by Pawel Czerwinski / Unsplash

In the mid-1990s, the web rapidly evolved, and the need for a more dynamic and interactive browsing experience became increasingly apparent. Netscape, the company behind the popular Mosaic web browser, recognized this shift and wanted to stay ahead of the curve.

Brendan Eich was then a young programmer with a background in programming languages and compilers. Eich's task was to create a new scripting language that would complement Netscape's browser, and allow developers to add interactivity and automation to web pages.

Eich aimed to democratize web development by providing a language that was easy to learn and use, yet powerful enough to enable complex web applications. He envisioned a world where web pages were no longer static, but rather dynamic and responsive to user interactions.

With this ambitious vision in mind, Eich designed and implemented a new language in just 10 days.

The result of this remarkable feat of engineering was JavaScript, a language that would become the de facto standard for client-side scripting on the web.

However, this rushed development process also left behind a curious quirk that would haunt developers for decades to come: the fact that typeof null === "object" in JavaScript.

This seemingly innocuous behavior has become a source of confusion, frustration, and even heated debates among JavaScript developers.

But how did we end up in this situation, and why is it so difficult to fix?

The Origins of the Problem

In JavaScript's initial implementation, values were represented in memory using a 32-bit format where the first 3 bits served as a type tag for type-checking operations.

Here is an example of integer number 1:

Full 32-bit binary:
00100000000000000000000000000001

32-bit binary consists of:
- First 3 bits: 001 (integer type tag)
- Remaining 29 bits: 00000000000000000000000000001 (actual value of 1)

In hexadecimal: 0x20000001

Memory layout visualization:
┌─────────┬───────────────────────────────┐
│ Type Tag│           Value               │
├─────────┼───────────────────────────────┤
│   001   │ 00000000000000000000000000001 │
└─────────┴───────────────────────────────┘
  (3 bits)         (29 bits)

The type system used the following binary patterns:

  • 001 - integer
  • 010 - double
  • 110 - boolean
  • 100 - string
  • 000 - object

The implementation detail that led to the infamous typeof null behavior was rooted in memory representation.

null was implemented as a null pointer, represented as 0x00000000 in memory (32 zeros in binary).

When the typeof operator performs type checking, it masks the first three bits of the value.

For null, these bits are 000, which matches the object type tag in the type system.

This leads to the behavior where typeof null returns "object".

Despite being identified as a design flaw early on (since null conceptually represents the absence of any value rather than an object), the behavior was retained to maintain backward compatibility with existing codebases.

This technical decision has persisted in the language specification for nearly 3 decades.

The Legacy of a "Temporary" Solution

The decision was made to keep this behavior, as changing it would have broken a significant amount of existing JavaScript code.

And so, the "bug" remained.

Over the years, as JavaScript grew in popularity and became an integral part of the web, this quirky behavior became deeply entrenched in the language. Developers started to rely on it, either by consciously using it or simply accepting it as a part of the language's eccentricities.

Today, millions of websites and applications depend on this behavior. Attempting to fix it would risk breaking vast swaths of the internet, making it a virtually impossible task.

The Implications for Developers

The persistence of typeof null === "object" has had far-reaching implications for JavaScript developers.

It has led to confusion and surprises as many developers, especially those new to the language, are caught off guard by this behavior, leading to unexpected results and debugging challenges.

To work around this issue, developers have come up with various solutions, workarounds, and hacks, from explicit type checks to using the === operator instead of ==.

The inability to fix this problem has limited the language's evolution, as making changes that would break existing code is often not feasible.

A Cautionary Tale

The story of typeof null === "object" is a cautionary tale for all developers.

It serves as a reminder that the decisions we make, even for seemingly minor implementation details, can have far-reaching consequences.

The lesson is to think twice and code once.

Carefully consider the long-term implications of your design choices, and strive to build systems that are not only functional but also maintainable and adaptable.

Quick decisions in code can create decades of legacy.

"Temporary solutions" might outlive your career, leaving future developers to fight with the consequences.

Nothing is more permanent than a temporary solution.