🤪 A list of funny and tricky JavaScript examples
[![WTFPL 2.0][license-image]][license-url] [![NPM version][npm-image]][npm-url] [![Patreon][patreon-image]][patreon-url] [![Buy Me A Coffee][bmc-image]][bmc-url]
A list of funny and tricky JavaScript examples
JavaScript is a great language. It has a simple syntax, large ecosystem and, what is most important, a great community.
At the same time, we all know that JavaScript is quite a funny language with tricky parts. Some of them can quickly turn our everyday job into hell, and some of them can make us laugh out loud.
The original idea for WTFJS belongs to Brian Leroux. This list is highly inspired by his talk “WTFJS” at dotJS 2012:
You can install this handbook using npm. Just run:
$ npm install -g wtfjsYou should be able to run wtfjs at the command line now. This will open the manual in your selected $PAGER. Otherwise, you may continue reading on here.
The source is available here: https://github.com/denysdovhan/wtfjs
Currently, there are these translations of wtfjs:
Help translating to your language
Note: Translations are maintained by their translators. They may not contain every example, and existing examples may be outdated.
[] is equal ![]true is not equal ![], but not equal [] tooNaN is not a NaNObject.is() and === weird cases[] is truthy, but not truenull is falsy, but not falsedocument.all is an object, but it is undefinedundefined and NumberparseInt is a bad guytrue and falseNaN is [] and null are objects0.1 + 0.2Stringconstructor property__proto__`${{Object}}`try..catcharguments and arrow functionsNumber.toFixed() display different numbersMath.max() less than Math.min()null to 0{}{} is undefinedarguments bindingalert from hellsetTimeout objecttrueJust for fun
— “Just for Fun: The Story of an Accidental Revolutionary”, Linus Torvalds
The primary goal of this list is to collect some crazy examples and explain how they work, if possible. Just because it's fun to learn something that we didn't know before.
If you are a beginner, you can use these notes to get a deeper dive into JavaScript. I hope these notes will motivate you to spend more time reading the specification.
If you are a professional developer, you can consider these examples as a great reference for all of the quirks and unexpected edges of our beloved JavaScript.
In any case, just read this. You're probably going to find something new.
⚠️ Note: If you enjoy reading this document, please, consider supporting the author of this collection.
// -> is used to show the result of an expression. For example:
1 + 1; // -> 2// > means the result of console.log or another output. For example:
console.log("hello, world!"); // > hello, world!// is just a comment used for explanations. Example:
// Assigning a function to foo constant
const foo = function() {};[] is equal ![]Array is equal not array:
[] == ![]; // -> trueThe abstract equality operator converts both sides to numbers to compare them, and both sides become the number 0 for different reasons. Arrays are truthy, so on the right, the opposite of a truthy value is false, which is then coerced to 0. On the left, however, an empty array is coerced to a number without becoming a boolean first, and empty arrays are coerced to 0, despite being truthy.
Here is how this expression simplifies:
+[] == +![];
0 == +false;
0 == 0;
true;See also [] is truthy, but not true.
true is not equal ![], but not equal [] tooArray is not equal true, but not Array is not equal true too;
Array is equal false, not Array is equal false too:
true == []; // -> false
true == ![]; // -> false
false == []; // -> true
false == ![]; // -> truetrue == []; // -> false
true == ![]; // -> false
// According to the specification
true == []; // -> false
toNumber(true); // -> 1
toNumber([]); // -> 0
1 == 0; // -> false
true == ![]; // -> false
![]; // -> false
true == false; // -> falsefalse == []; // -> true
false == ![]; // -> true
// According to the specification
false == []; // -> true
toNumber(false); // -> 0
toNumber([]); // -> 0
0 == 0; // -> true
false == ![]; // -> true
![]; // -> false
false == false; // -> true!!"false" == !!"true"; // -> true
!!"false" === !!"true"; // -> trueConsider this step-by-step:
// true is 'truthy' and represented by value 1 (number), 'true' in string form is NaN.
true == "true"; // -> false
false == "false"; // -> false
// 'false' is not the empty string, so it's a truthy value
!!"false"; // -> true
!!"true"; // -> true"b" + "a" + +"a" + "a"; // -> 'baNaNa'This is an old-school joke in JavaScript, but remastered. Here's the original one:
"foo" + +"bar"; // -> 'fooNaN'The expression is evaluated as 'foo' + (+'bar'), which converts 'bar' to not a number.
NaN is not a NaNNaN === NaN; // -> falseThe specification strictly defines the logic behind this behavior:
- If
Type(x)is different fromType(y), return false.- If
Type(x)is Number, then
- If
xis NaN, return false.- If
yis NaN, return false.- … … …
Following the definition of NaN from the IEEE:
Four mutually exclusive relations are possible: less than, equal, greater than, and unordered. The last case arises when at least one operand is NaN. Every NaN shall compare unordered with everything, including itself.
— “What is the rationale for all comparisons returning false for IEEE754 NaN values?” at StackOverflow
Object.is() and === weird casesObject.is() determines if two values have the same value or not. It works similar to the === operator but there are a few weird cases:
Object.is(NaN, NaN); // -> true
NaN === NaN; // -> false
Object.is(-0, 0); // -> false
-0 === 0; // -> true
Object.is(NaN, 0 / 0); // -> true
NaN === 0 / 0; // -> falseIn JavaScript lingo, NaN and NaN are the same value but they're not strictly equal. NaN === NaN being false is apparently due to historical reasons so it would probably be better to accept it as it is.
Similarly, -0 and 0 are strictly equal, but they're not the same value.
For more details about NaN === NaN, see the above case.
You would not believe, but …
(![] + [])[+[]] +
(![] + [])[+!+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[]Stream chunking anti-pattern in `wtfjs.js` causes Markdown/UTF-8 corruption and duplicate banners
"Adding arrays" explanation code in `README.md` throws runtime `TypeError` due to ASI collision
Language flag (`--lang`) fails on case-sensitive filesystems due to uppercase subtag mapping
CLI crashes on launch with `TypeError: meow is not a function` due to ESM/CJS mismatch
Overloading the await keyword.
The automated release is failing
`Object.is(Math.sqrt(-0), -0) && Object.is((-0) ** +0.5, +0)`
Character data interpreted in XHTML
`Date`
`for..in` iterating array indices: `string`, Not just `number`