Skip to main content

Command Palette

Search for a command to run...

Why Does JavaScript Feel So AbSuRd? Part - 1

A weird lowkey autistic language like me .

Updated
11 min readView as Markdown
Why Does JavaScript Feel So AbSuRd? Part - 1
R
cracked noob dev . 21

Why Does JavaScript Feel So AbSuRd?

Try answering these all? [] == false // true 0.1 + 0.2 === 0.3 // false typeof null // "object" NaN === NaN // false 9007199254740992 === 9007199254740993 // true

Did not get at least one right? Happens man! You, me, we both haven't spent enough time with JavaScript. Let's change that!

JavaScript has some wonderfully weird corners. Some of them are genuinely useful. Some are historical baggage. And some look completely ridiculous until you understand the rules underneath them.

But here's the interesting part: most of these behaviors are consequences of specific design decisions, type coercion rules, floating point representation, backward compatibility, and a language that has been evolving for decades while trying very hard not to break the web.

[] == false is true. Why does JavaScript arrive at that answer? That's what this article is about.

This is Part 1 of my attempt to understand JavaScript beyond the syntax. We will start with numbers, then move into type coercion and operators, and try to understand why JavaScript behaves the way it does instead of simply memorizing a list of weird facts.


Before We Start, JavaScript Has History

One of the easiest mistakes when learning JavaScript is assuming that it was designed as one perfectly planned language. It wasn't. Although JavaScript has evolved a lot, it also has a lot of history packed into it.

It started as a relatively small scripting language and eventually became the language running enormous parts of the web, including browsers, servers, build tools, applications, frameworks, extensions, tests, and much more.

And once millions of websites depend on a behavior, you can't simply just say:

"Yeah, that was a terrible idea. Let's f*king remove it."

Because somewhere out there is probably a website that depends on it.

This explains a lot of JavaScript's personality. Older ideas coexist with newer ones: • var, let, const • Old equality == vs Modern strict equality === • Prototype based objects Object.create(...) and later, class syntax class User {} • Callbacks setTimeout(...) and much later async / await

JavaScript is less like a freshly designed building and more like a city. There are beautiful new buildings, ancient roads, strange shortcuts, and at least one intersection nobody knows how to remove because apparently somebody's production system depends on it.

And that's actually a useful way to learn JavaScript. Don't just ask what the syntax does. Ask why the language behaves that way.


1. Numbers Are Not Quite What You Think

Let's start with something that looks completely innocent: 0.1 + 0.2. You might expect 0.3. JavaScript says 0.30000000000000004. And 0.1 + 0.2 === 0.3 // false.

Let's understand what is happening. JavaScript's ordinary Number uses IEEE 754 double precision floating point representation. Computers don't store decimal numbers exactly the way humans write them.

We write 0.1, 0.2, 0.3, but floating point numbers are represented using binary. And just like 1 / 3 cannot be represented exactly using a finite number of decimal digits (0.333333333333...), some decimal fractions cannot be represented exactly in binary.

So JavaScript stores the closest representable value. That means the values involved in 0.1 + 0.2 are already approximations. The result is therefore approximately 0.30000000000000004.

This isn't uniquely a JavaScript problem. It's a consequence of floating point representation.

Comparing Floating Point Numbers Because of this, directly comparing floating point values can be dangerous. Instead of blindly doing a === b, you can compare whether they're sufficiently close: const nearlyEqual = (a, b, eps = Number.EPSILON) => Math.abs(a - b) < eps; nearlyEqual(0.1 + 0.2, 0.3); // true

The exact comparison strategy should depend on the scale and requirements of your calculation, but the important idea is simple: floating point arithmetic is approximate.


2. The Number Safe Integer Limit

JavaScript numbers have another interesting limitation: Number.MAX_SAFE_INTEGER gives 9007199254740991, which is 2^53 - 1.

Why can't JavaScript safely represent every integer beyond this? Because Number is a floating point format, and it has limited precision. Eventually, two different integers can round to the same representable floating point value.

For example: 9007199254740992 === 9007199254740993 // true.

That looks completely absurd. But the important word is precision here. The number isn't necessarily too large to exist. JavaScript simply can't represent every integer in that range distinctly using Number.

You can check whether an integer is safe: Number.isSafeInteger(2 ** 53); // false

For integers requiring arbitrary precision, JavaScript provides BigInt: const huge = 9007199254740993n; Notice the n. It indicates that this is an integer value represented using BigInt rather than Number.


3. NaN Is... Special

Now we reach one of JavaScript's favorite ways to confuse beginners like me: NaN === NaN // false. Wait whaaaaat! How can something not equal itself?

NaN represents a special numerical value that indicates an invalid or undefined numerical result. For example: 0 / 0 // NaN Number("hello") // NaN

NaN has special comparison semantics. So NaN === NaN // false. If you want to check whether something is actually NaN, use: Number.isNaN(NaN) // true

And notice the difference from the older global function: isNaN("hello") // true The global isNaN() performs coercion first. What the hell is coercion? We will come to that soon. Number.isNaN() does not: Number.isNaN("hello") // false It is generally much safer when you specifically want to know whether the value is the NaN value.


4. Infinity Exists Too

JavaScript's number system also includes Infinity, -Infinity, and NaN. So: 1 / 0 // Infinity -1 / 0 // -Infinity 0 / 0 // NaN

Again, these behaviors come from the floating point number model JavaScript uses.


5. Never Blindly Use Floating Point for Money

A weird JavaScript behavior becomes a real engineering problem when money gets involved. Imagine calculating 0.1 + 0.2 once. Not a big deal. Now imagine doing floating point calculations across thousands of financial transactions. Suddenly tiny representation errors can become actual financial defects.

A common solution is to represent money using integer minor units. Instead of ₹19.99, store 1999 paise. Then arithmetic remains integer arithmetic and you divide by 100 only for display.

For calculations requiring decimal arithmetic, use an appropriate decimal representation or library and a suitable database type rather than blindly relying on binary floating point. A tiny floating point quirk can become a very non tiny production bug.


6. Type Coercion, "I'll Figure It Out" Philosophy (lol)

Now let's talk about one of JavaScript's most famous features: type coercion. Here we go.

JavaScript can automatically convert values from one type to another depending on the operation being performed. For example: 1 == "1" // true 1 === "1" // false

What's happening? This is where JavaScript starts doing something that feels strange at first. The language can look at two values with different types and, depending on the operator, convert one or both values before comparing them. That automatic conversion is called type coercion.


7. == vs ===

The easiest way to think about them is: • == → allows type coercion • === → does not perform that coercion

So 1 == "1" // true can involve converting the string "1" → 1 and then comparing 1 == 1. But 1 === "1" // false because one value is a number and the other is a string.

This is why modern JavaScript code generally prefers === and !==. It makes the comparison more explicit and avoids many surprising conversions.


8. The Famous null and undefined Case

Here's one of the few places where == can actually be useful: null == undefined // true But: null === undefined // false

They're different values, but loose equality has a special rule treating them as equivalent. So this: value == null can intentionally mean: "Is this either null or undefined?" Equivalent to: value === null || value === undefined

This is one of the rare cases where using == can be intentional and readable. Everywhere else? I'd generally reach for ===.


9. And Then JavaScript Does This

[] == false // true. At this point, JavaScript appears to have completely lost the plot. But let's slow down.

This isn't because an empty array is secretly false. In fact: Boolean([]) // true. An empty array is truthy.

So why does [] == false produce true? Because == uses its own coercion rules. The object is converted toward a primitive value. Conceptually, the empty array can become: []""0 And false0. So the comparison eventually becomes 0 == 0, which is true.

This is an important distinction. Equality coercion and boolean coercion are not the same thing. When JavaScript evaluates if ([]), it asks whether the value is truthy. Since an array is an object: Boolean([]) // true.

But when evaluating [] == false, JavaScript follows the abstract equality algorithm, which involves different conversion rules. Same value. Different operation. Different rules. And that is a recurring theme in JavaScript.


10. Truthy and Falsy

There are exactly eight falsy values: • false, 0, -0, 0n, "", null, undefined, NaN

Everything else is truthy. That means these are all truthy: • [], {}, "0", "false", function () {}

Yes. Even Boolean([]) // true and Boolean({}) // true. Objects are truthy regardless of whether they're "empty." This is why: if ([]) { // runs }

Understanding truthy and falsy values is important because JavaScript uses boolean conversion in many places, including if, while, logical operators, and conditional expressions.


11. Explicit Conversion Is Usually Better

JavaScript can automatically convert values, but you can also explicitly convert them. And explicit conversion is usually easier to understand: String(42) // "42" Number("42") // 42 Boolean(0) // false

Some interesting examples: Number("") // 0 Number(" 12 ") // 12 Number("12px") // NaN

For parsing: parseInt("12px", 10) // 12 Notice the radix: parseInt("12", 10). It's good practice to explicitly provide it. And parseFloat("3.14em") // 3.14.

JavaScript is willing to parse the numeric prefix here. That can be useful, but it can also be surprising if you expected strict validation.

The bigger lesson is that explicit conversion makes your intention visible. Instead of making the reader figure out what JavaScript might convert automatically, you can tell JavaScript exactly what type you want.


12. Operators Have Their Own Personality

JavaScript operators don't always simply return booleans. Consider: const name = input || "Anonymous"; The || operator returns one of its operands. It isn't simply true or false.

Similarly: const port = process.env.PORT ?? 3000; uses the nullish coalescing operator.

The difference between || and ?? is extremely important. Consider: const count = 0; count || 10 // 10 (because 0 is falsy) count ?? 10 // 0 (because ?? only falls back for null and undefined)

So: • || → fallback if falsy • ?? → fallback if null or undefined

If 0 is a legitimate value, ?? is often what you actually want.

Logical Assignment JavaScript extends these ideas with logical assignment: a ||= b; a &&= b; a ??= b;

Conceptually: • a ||= b; means roughly a = a || b; (Assign b if a is falsy) • a &&= b; means roughly a = a && b; (Assign b if a is truthy) • a ??= b; means roughly a = a ?? b; (Assign b only if a is null or undefined)

These operators are small, but they become extremely useful once you understand the underlying behavior.


Where We Stop for Part 1

So far, we've looked at the part of JavaScript that makes many people wonder whether the language was designed during a very long coffee break.

We've seen floating point precision, safe integers, NaN, Infinity, type coercion, loose and strict equality, truthy and falsy values, explicit conversion, nullish coalescing, and logical assignment.

But there is a much more interesting part of JavaScript waiting ahead: functions. Not just how to declare them, but why JavaScript treating functions as values changes almost everything.

In the next part, we'll get into first class functions, higher order functions, reduce(), closures, currying, function composition, pipe(), arrow functions, this, destructured parameters, and more.

And that's where JavaScript starts becoming less about syntax and more about how the language actually thinks.

See you in Part 2.

Understanding an AuTiStiC Language (JavaScript)

Part 1 of 3

Going beyond syntax and figuring out why JavaScript works the way it does with cool examples .

Up next

Why Does JavaScript Feel So AbSuRd? Part - 2

A weird lowkey autistic language like me .

More from this blog

Why Does JavaScript Feel So AbSuRd?

3 posts

A curious journey into JavaScript uncovering the weird, powerful, and surprisingly logical parts of the language. We’ll go beyond syntax to understand the ideas, history, and machinery behind the code.