Meme Review: Thanks For Inventing JavaScript
Let's break down each example from the meme in detail:
typeof NaN
Output: "number"
In JavaScript, NaN
is actually a special numeric value representing an invalid or unrepresentable number. NaN
is part of the IEEE-754 floating-point number specification used by JavaScript.
9999999999999999
Output: 10000000000000000
This is a perfect example of JavaScript's number precision limitations. JavaScript uses 64-bit floating-point numbers (IEEE-754), which can only precisely represent integers up to 2^53 - 1 which is 9007199254740991
. Beyond that, number considered as unsafe and in some examples incorrect rounding may occur.
If you want to verify whether a number is safe, you can use the isSafeInteger
method: Number.isSafeInteger(9007199254740992)
.
0.5+0.1==0.6
Output: true
This comparison works correctly because these particular decimal numbers can be precisely represented in binary floating-point. It's one of the "well-behaved" floating-point calculations.
0.1+0.2==0.3
Output: false
This is perhaps one of the most famous JavaScript quirks. Due to how floating-point numbers are stored in binary, 0.1 and 0.2 cannot be precisely represented. When added, they actually result in 0.30000000000000004, which is not exactly equal to 0.3.
Math.max()
Output: -Infinity
When called with no arguments, Math.max()
returns -Infinity. This makes sense from a mathematical perspective: any other number you compare with -Infinity will be greater, making it a suitable initial value for finding a maximum.
Math.min()
Output: Infinity
The counterpart to Math.max()
, Math.min()
returns Infinity when called with no arguments. Any other number compared to Infinity will be smaller.
[]+[]
Output: ""
When you add two arrays, JavaScript first converts them to strings. An empty array converts to an empty string, so you're effectively concatenating two empty strings.
You can check formalized version of the coercion algorithm here
[]+{}
Output: "[object Object]"
Here, the empty array converts to an empty string, and the empty object converts to the string "[object Object]". These are then concatenated:
{}+[]
Output: 0
This one is about how the expression is being parsed to AST. When {} appears at the start of a line/statement, JavaScript's parser sees this as two separate entities:
- An empty code block {}
- The expression +[]
The first {}
is interpreted as an empty code block (not an object), and +[]
(unary plus operation on an array) converts the empty array to a number, which is 0
. The code block doesn't result in a value, so 0
is the output.
You can check formalized version of the unary coercion algorithm here
true+true+true===3
Output: true
In numeric contexts, true
converts to 1
and false
to 0
. Therefore, true + true + true
is evaluated as 1 + 1 + 1
, making this expression equivalent to 1+1+1===3
.
true==1
Output: true
When using loose equality (==
), JavaScript performs type coercion. true
gets converted to 1
, making this comparison true
.
See rules of == conversion in details here
true===1
Output: false
With strict equality (===
), no type coercion occurs. Since true
is a boolean and 1
is a number, they're not the same type, so the comparison is false
.
(!+[]+[]+![]).length
Output: 9
This is a complex example of type coercion:
!+[]
evaluates tofalse
because+[]
, evaluates to0
, and!0
evaluates tofalse
[]
evaluates to""
![]
evaluates tofalse
- Concatenating them all gives
falsefalse
, sincefalse + "" + false
concatenates the strings together. - The length of "falsefalse" is 9
9+"1"
Output: "91"
When adding a number to a string, JavaScript converts the number to a string and performs concatenation.
91-"1"
Output: 90
The minus operator -
only has one purpose - subtraction. It always converts its operands to numbers, so "1"
becomes 1
, and 91-1
equals 90
.
[]==0
Output: true
Through a series of type coercions:
[]
becomes""
""
becomes0
0
equals0
true-true
Output: 0
Both true
values are converted to 1
, so this becomes 1-1=0
.
Rules Of +
Operator
The +
operator in JavaScript can perform both addition and string concatenation, depending on the types of its operands. Here's a simplified view of the coercion process, as specified by the ECMAScript specification:
Primitive Conversion:
The first step is converting both operands to primitive values:
Objects (including Arrays):
When an object (or Array, as it's technically an 'object' at its root) is involved, JavaScript:
- First attempts to call its
valueOf()
method to get a primitive value - If
valueOf()
returns a primitive value, uses that result - If not, calls the object's
toString()
method instead
toString()
:
Arrays don't have a valueOf()
method that returns a primitive value, so they proceed directly to calling toString()
- For arrays,
toString()
produces a comma-separated string of its contents, or an empty string""
for empty arrays - Other Primitive Types: These require no conversion as they're already primitive
Type Check and Operation: After primitive conversion:
- If either operand is a string: The
+
operator performs string concatenation - If both operands are numbers: The
+
operator performs numeric addition
Rules of unary +
Operator Coercion
Step 1: Unary Plus Operation
The +
operator in front of []
is a unary plus operator, which attempts to convert its operand to a number.
Step 2: Array to Primitive Conversion
When converting an array to a primitive for numeric operations:
- First, JavaScript tries to call
valueOf()
on the array- For arrays,
valueOf()
just returns the array itself, which is not primitive
- For arrays,
- Since
valueOf()
didn't give a primitive, it callstoString()
[].toString()
converts an empty array to an empty string""
Step 3: String to Number Conversion
Now that we have an empty string ""
, the unary plus converts it to a number:
- Empty string
""
converts to0
when converted to a number
Rules Of == Coercion
The ==
(loose equality) coercion in JavaScript follows a specific algorithm defined in the ECMAScript specification. Here's the simplified flow:
Basic Rules of == Coercion
If types are the same:
null == null // true undefined == undefined // true true == true // true 5 == 5 // true "foo" == "foo" // true
If comparing null and undefined:
null == undefined // true undefined == null // true
If one operand is a number and one is a string:
- Convert string to number
5 == "5" // true (converts "5" to 5) "5" == 5 // true (converts "5" to 5)
If one operand is boolean:
- Convert boolean to number (true → 1, false → 0)
true == 1 // true (converts true to 1) false == 0 // true (converts false to 0)
If one operand is object and other is primitive:
- Convert object to primitive using
valueOf()
ortoString()
[1,2] == "1,2" // true (array converts to "1,2" string) [] == "" // true (empty array converts to empty string) [0] == 0 // true ([0] → "0" → 0)
- Convert object to primitive using