After writing about PHP's greatest hits in the Weird Language Hall of Fame, it felt only fair to turn to JavaScript.
Because if PHP is the language of historical baggage, JavaScript is the language of "this seems impossible, and yet it is fully specified behavior."
JavaScript has a special talent for producing results that look like bugs, feel like bugs, and absolutely are not bugs.
Most of the time, the culprit is type coercion, implicit conversion, or one of the language's many "technically reasonable, emotionally devastating" rules.
So here are a few of my favorite examples.
1. An Empty Array Is Truthy... But Also Equal to false
Let's begin with a classic.
Boolean([]) // true
[] == false // true
[] === false // false
This is the kind of thing that makes developers stare at the screen and wonder why they picked computing science instead of accounting like their mom advised.
An empty array is truthy.
So far, so normal:
if ([]) {
console.log("Yep, truthy");
}
That runs.
But then this also happens:
[] == false // true
Why?
Because JavaScript doesn't use the same rules for boolean contexts and loose equality.
In a boolean context, arrays are simply objects, and objects are truthy.
But with ==, JavaScript starts converting values. The empty array becomes an empty string, which then becomes the number 0. Meanwhile, false also becomes 0.
So JavaScript ends up comparing:
0 == 0
Which is, unfortunately, true.
And if you really want to ruin your afternoon:
[] == ![] // true
Because ![] becomes false, and now you're back in the previous example.
The lesson: truthiness and loose equality are not the same thing. If you're comparing values in JavaScript, === should be your default.
2. typeof null Is "object"
This one has survived for decades.
typeof null // "object"
typeof undefined // "undefined"
typeof [] // "object"
typeof NaN // "number"
Let's focus on the weirdest part:
typeof null // "object"
Except null is not an object.
This is a long-standing historical bug in JavaScript that became untouchable because too much code depended on it.
So now it just lives there, like haunted wallpaper.
And since JavaScript enjoys layering confusion on top of confusion:
-
nullis not an object, buttypeof nullsays it is. - Arrays are objects, so
typeof []is also"object". -
NaNmeans "Not a Number," buttypeof NaNis"number".
You can almost hear the language whispering, "If you can't handle me on my worst days, you don't deserve me."
The lesson: typeof is useful, but not always precise. If you need to distinguish arrays, nulls, and objects, use more specific checks like Array.isArray() and explicit value === null.
3. map(parseInt) Produces Nonsense
This is one of the best examples in the entire language because every piece of it looks reasonable.
["10", "10", "10"].map(parseInt)
What would you expect?
[10, 10, 10]
What do you actually get?
[10, NaN, 2]
Naturally.
Here's what's happening.
map() doesn't just pass each array element to the callback. It passes three arguments:
- the current value
- the current index
- the whole array
So this:
["10", "10", "10"].map(parseInt)
effectively becomes:
parseInt("10", 0) // 10
parseInt("10", 1) // NaN
parseInt("10", 2) // 2
And why is that a problem?
Because parseInt() takes a second argument: the radix.
So JavaScript interprets the array index as the number base.
-
"10"in base 0 becomes 10 -
"10"in base 1 is invalid, soNaN -
"10"in base 2 becomes 2
This is one of those perfect JavaScript moments where two completely legitimate APIs collide and create chaos.
The fix is simple:
["10", "10", "10"].map(Number)
or:
["10", "10", "10"].map(x => parseInt(x, 10))
The lesson: Don't hand a function to map() unless you know exactly what arguments it expects.
4. sort() Sorts Numbers Like Strings
This one has caused real bugs in real applications.
[1, 2, 10, 20, 3].sort()
What you probably expect:
[1, 2, 3, 10, 20]
What JavaScript gives you:
[1, 10, 2, 20, 3]
Because by default, sort() compares elements as strings.
So JavaScript is effectively sorting these:
"1""2""10""20""3"
And in string comparison, "10" comes before "2".
This makes perfect sense if you're sorting words.
It makes much less sense if you're sorting numbers in a programming language and expected numeric order to be the default.
The fix:
[1, 2, 10, 20, 3].sort((a, b) => a - b)
Now you get what you wanted:
[1, 2, 3, 10, 20]
The lesson: JavaScript's default behaviors are often optimized for generality, not for your expectations.
So, Is JavaScript Bad?
Not really.
Like PHP, JavaScript is often mocked for behavior that is actually consistent with its own rules.
The problem is that many of those rules are invisible until you trip over them.
An empty array can be truthy while also loosely equal to false.
typeof null can lie to your face.
A perfectly innocent-looking map(parseInt) can produce results that seem like a prank.
And sort() can rearrange numbers alphabetically, because apparently that was a reasonable default choice at some point in human history.
But that's also what makes JavaScript fascinating.
Its weirdness isn't random. It's the product of a language that was created to validate forms in web pages and became something people use to build microservices and large-scale data streaming pipelines, and evolved while the entire internet was standing on top of it.
Which is kind of impressive.
This is part of a series on programming languages that occasionally make developers question the nature of reality.
PHP already had its turn. Python, C#, and Perl are waiting patiently in the wings.
And Perl, as always, remains less a programming language than a dare.
Top comments (0)