Why JavaScript Has Quirks (And Why TC39 Can't Fix Them)

Search for a command to run...

No comments yet. Be the first to comment.
Quick Answer: SQL injection attacks exploit vulnerabilities in how web applications construct database queries. By sending malicious SQL code through user input fields (like search bars), attackers ca

Stop wasting your context window explaining industry standards to LLMs. Large Language Models already have best practices baked into their training data; you just need to activate those specific regio

The Netflix Keeper Test asks managers: "If this engineer resigned today, would you fight to keep them?" While it ensures high talent density, I believe it often destroys psychological safety. For most

Quick Answer: No, AI agents won't replace software engineers, but they are flipping the table on how we work. While agents excel at writing generic, path-of-least-resistance code, they lack opinion. H

TL;DR: JavaScript has notorious quirks because it was designed in just ten days as a prototype to help Netscape survive the early browser wars. Because the internet relies on absolute backwards compatibility, these rushed design flaws became permanent APIs that we must support forever to avoid breaking the web.
I've lost count of the times I've stared at a console, questioning my career choices because of some bizarre JavaScript coercion behavior. Maybe it is the fact that typeof null returns "object", or that sorting an array of numbers alphabetically arranges them as [1, 10, 2, 20].
Whenever I am digging through these quirks, I try to remind myself of how we got here. I find it absolutely mad that the language powering almost every modern user interface on earth was built as a rushed, two-week prototype. It is the ultimate real-world case of "protoduction"—a temporary hack thrown directly into production under existential company pressure.
JavaScript has so many design flaws because it was designed and implemented in just ten days by Brendan Eich at Netscape in 1995. Rushed to market to win the early browser wars against Microsoft, the language was never intended to be the final product, meaning early prototype compromises became permanent.
To understand why these quirks exist, I always look back to the mid-1990s browser wars. Imagine your team is building a web browser, and your company's survival depends entirely on shipping a scripting language before your competitors do. If you do not get there first, your product is dead.
That was the pressure cook Brendan Eich was under at Netscape. I think any engineer can sympathize with that level of stress. He had to compromise on language design, cobbling together elements of Scheme, Self, and Java in less than two weeks. It worked brilliantly for the business, but it left us with shortcuts that were never meant to survive the weekend, let alone three decades.
// I love this classic symptom of a 10-day deadline:
console.log(typeof null); // "object"
console.log([] + {}); // "[object Object]"
TC39 cannot remove legacy quirks because doing so would break millions of existing websites that rely on those exact behaviors. The web operates on a strict rule of backwards compatibility, meaning we can only add new features to the language, never delete old ones.
I often hear developers ask why we don't just run a massive cleanup on the specification. The reality is that the web's core philosophy is strict backwards compatibility.
I like to use the analogy of a historical city. Imagine you have a cardboard bridge built decades ago. It was only meant to be temporary, but over the years, thousands of delivery trucks started crossing it daily. You cannot tear it down because those trucks—representing millions of legacy websites—would crash into the river. All you can do is build a modern steel bridge right next to it, point at the cardboard one, and warn people not to use it.
| Quirk | Original Root Cause | Modern Mitigation |
|---|---|---|
typeof null === 'object' |
A bug in the original 1995 type tag implementation. | I use strict null checks (=== null). |
| Global variable leakage | Rushed scope resolution that defaults to the global object. | I enforce strict mode ("use strict") and block-scoped let/const. |
| Automatic Semicolon Insertion | An attempt to make the language forgiving for non-programmers. | I rely on linters (like ESLint) to enforce consistent styling. |
Modern JavaScript evolves by layering clean, opt-in APIs and syntax over the legacy core rather than modifying the underlying behaviors. Standards committees use tools like strict mode, compiler flags, and transpilars to allow developers to write clean code while preserving the old runtime environment.
I've watched the ecosystem shift toward build-time tooling to solve this. Instead of trying to fix the runtime engine, we use tools like Babel and TypeScript to write clean, modern, type-safe code that compiles down to the highly compatible (and highly quirky) JavaScript that browsers actually run.
We are essentially papering over the cracks. When I look at modern ES6+ features, I see a beautiful, highly capable language, but it is built entirely on top of a 10-day prototype. It is a remarkable engineering feat that we have managed to scale this temporary bridge to support the entire digital world.
Netscape was in an intense race against Microsoft to establish the dominant web platform. They needed a lightweight scripting language to pair with their browser immediately, forcing developer Brendan Eich to deliver a working prototype under an impossibly tight deadline.
"Don't break the web" is the core philosophy of web standards bodies. It means that any code written in 1995 must run in a modern browser today without modification, preventing developers from removing old, buggy features from the JavaScript specification.
While WebAssembly (Wasm) allows languages like Rust, C++, and Go to run in the browser at near-native speeds, it is designed to complement JavaScript rather than replace it. JavaScript remains the only native language for direct DOM manipulation and web scripting.