# wtfjs **Repository Path**: mirrors_shlomif/wtfjs ## Basic Information - **Project Name**: wtfjs - **Description**: A list of funny and tricky JavaScript examples - **Primary Language**: Unknown - **License**: WTFPL - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-09-25 - **Last Updated**: 2026-03-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # What the f*ck JavaScript? [![WTFPL 2.0][license-image]][license-url] [![NPM version][npm-image]][npm-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](https://twitter.com/brianleroux). This list is highly inspired by his talk [**“WTFJS”** at dotJS 2012](https://www.youtube.com/watch?v=et8xNAc2ic8): [![dotJS 2012 - Brian Leroux - WTFJS](https://img.youtube.com/vi/et8xNAc2ic8/0.jpg)](https://www.youtube.com/watch?v=et8xNAc2ic8) # Node Packaged Manuscript You can install this handbook using `npm`. Just run: ``` $ npm install -g wtfjs ``` You 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: # Table of Contents - [💪🏻 Motivation](#-motivation) - [✍🏻 Notation](#-notation) - [👀 Examples](#-examples) - [`[]` is equal `![]`](#-is-equal-) - [true is false](#true-is-false) - [baNaNa](#banana) - [`NaN` is not a `NaN`](#nan-is-not-a-nan) - [It's a fail](#its-a-fail) - [`[]` is truthy, but not `true`](#-is-truthy-but-not-true) - [`null` is falsy, but not `false`](#null-is-falsy-but-not-false) - [`document.all` is an object, but it's undefined](#document-all-is-an-object-but-it-is-undefined) - [Minimal value is greater than zero](#minimal-value-is-greater-than-zero) - [function is not function](#function-is-not-function) - [Adding arrays](#adding-arrays) - [Trailing commas in array](#trailing-commas-in-array) - [Array equality is a monster](#array-equality-is-a-monster) - [`undefined` and `Number`](#undefined-and-number) - [`parseInt` is a bad guy](#parseint-is-a-bad-guy) - [Math with `true` and `false`](#math-with-true-and-false) - [HTML comments are valid in JavaScript](#html-comments-are-valid-in-javascript) - [`NaN` is ~~not~~ a number](#nan-is-not-a-number) - [`[]` and `null` are objects](#-and-null-are-objects) - [Magically increasing numbers](#magically-increasing-numbers) - [Precision of `0.1 + 0.2`](#precision-of-01--02) - [Patching numbers](#patching-numbers) - [Comparison of three numbers](#comparison-of-three-numbers) - [Funny math](#funny-math) - [Addition of RegExps](#addition-of-regexps) - [Strings aren't instances of `String`](#strings-arent-instances-of-string) - [Calling functions with backticks](#calling-functions-with-backticks) - [Call call call](#call-call-call) - [A `constructor` property](#a-constructor-property) - [Object as a key of object's property](#object-as-a-key-of-objects-property) - [Accessing prototypes with `__proto__`](#accessing-prototypes-with-__proto__) - [``` `${{Object}}` ```](#-object-) - [Destructuring with default values](#destructuring-with-default-values) - [Dots and spreading](#dots-and-spreading) - [Labels](#labels) - [Nested labels](#nested-labels) - [Insidious `try..catch`](#insidious-trycatch) - [Is this multiple inheritance?](#is-this-multiple-inheritance) - [A generator which yields itself](#a-generator-which-yields-itself) - [A class of class](#a-class-of-class) - [Non-coercible objects](#non-coercible-objects) - [Tricky arrow functions](#tricky-arrow-functions) - [`arguments` and arrow functions](#arguments-and-arrow-functions) - [Tricky return](#tricky-return) - [Accessing object properties with arrays](#accessing-object-properties-with-arrays) - [Null and Relational Operators](#null-and-relational-operators) - [`Number.toFixed()` display different numbers](#numbertofixed-display-different-numbers) - [Other resources](#other-resources) - [🎓 License](#-license) # 💪🏻 Motivation > Just for fun > > — _[**“Just for Fun: The Story of an Accidental Revolutionary”**](https://en.wikipedia.org/wiki/Just_for_Fun), 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. # ✍🏻 Notation **`// ->`** is used to show the result of an expression. For example: ```js 1 + 1 // -> 2 ``` **`// >`** means the result of `console.log` or another output. For example: ```js console.log('hello, world!') // > hello, world! ``` **`//`** is just a comment used for explanations. Example: ```js // Assigning a function to foo constant const foo = function () {} ``` # 👀 Examples ## `[]` is equal `![]` Array is equal not array: ```js [] == ![] // -> true ``` ### 💡 Explanation: * [**12.5.9** Logical NOT Operator (`!`)](https://www.ecma-international.org/ecma-262/#sec-logical-not-operator) * [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## true is false ```js !!'false' == !!'true' // -> true !!'false' === !!'true' // -> true ``` ### 💡 Explanation: Consider this step-by-step: ```js // 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 ``` * [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## baNaNa ```js 'b' + 'a' + + 'a' + 'a' ``` This is an old-school joke in JavaScript, but remastered. Here's the original one: ```js 'foo' + + 'bar' // -> 'fooNaN' ``` ### 💡 Explanation: The expression is evaluated as `'foo' + (+'bar')`, which converts `'bar'` to not a number. * [**12.8.3** The Addition Operator (`+`)](https://www.ecma-international.org/ecma-262/#sec-addition-operator-plus) * [12.5.6 Unary + Operator](https://www.ecma-international.org/ecma-262/#sec-unary-plus-operator) ## `NaN` is not a `NaN` ```js NaN === NaN // -> false ``` ### 💡 Explanation: The specification strictly defines the logic behind this behavior: > 1. If `Type(x)` is different from `Type(y)`, return **false**. > 2. If `Type(x)` is Number, then > 1. If `x` is **NaN**, return **false**. > 2. If `y` is **NaN**, return **false**. > 3. … … … > > — [**7.2.14** Strict Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-strict-equality-comparison) 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?”](https://stackoverflow.com/questions/1565164/1573715#1573715) at StackOverflow ## It's a fail You would not believe, but … ```js (![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]] // -> 'fail' ``` ### 💡 Explanation: By breaking that mass of symbols into pieces, we notice that the following pattern occurs often: ```js (![]+[]) // -> 'false' ![] // -> false ``` So we try adding `[]` to `false`. But due to a number of internal function calls (`binary + Operator` -> `ToPrimitive` -> `[[DefaultValue]]`) we end up converting the right operand to a string: ```js (![]+[].toString()) // 'false' ``` Thinking of a string as an array we can access its first character via `[0]`: ```js 'false'[0] // -> 'f' ``` The rest is obvious, but the `i` is tricky. The `i` in `fail` is grabbed by generating the string `'falseundefined'` and grabbing the element on index `['10']` ## `[]` is truthy, but not `true` An array is a truthy value, however, it's not equal to `true`. ```js !![] // -> true [] == true // -> false ``` ### 💡 Explanation: Here are links to the corresponding sections in the ECMA-262 specification: * [**12.5.9** Logical NOT Operator (`!`)](https://www.ecma-international.org/ecma-262/#sec-logical-not-operator) * [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## `null` is falsy, but not `false` Despite the fact that `null` is a falsy value, it's not equal to `false`. ```js !!null // -> false null == false // -> false ``` At the same time, other falsy values, like `0` or `''` are equal to `false`. ```js 0 == false // -> true '' == false // -> true ``` ### 💡 Explanation: The explanation is the same as for previous example. Here's the corresponding link: * [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) ## `document.all` is an object, but it is undefined > ⚠️ This is part of the Browser API and won't work in a Node.js environment ⚠️ Despite the fact that `document.all` is an array-like object and it gives access to the DOM nodes in the page, it responds to the `typeof` function as `undefined`. ```js document.all instanceof Object // -> true typeof document.all // -> 'undefined' ``` At the same time, `document.all` is not equal to `undefined`. ```js document.all === undefined // -> false document.all === null // -> false ``` But at the same time: ```js document.all == null // -> true ``` ### 💡 Explanation: > `document.all` used to be a way to access DOM elements, in particular with old versions of IE. While it has never been a standard it was broadly used in the old age JS code. When the standard progressed with new APIs (such as `document.getElementById`) this API call became obsolete and the standard commitee had to decide what to do with it. Because of its broad use they decided to keep the API but introduce a willful violation of the JavaScript specification. > The reason why it responds to `false` when using the [Strict Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-strict-equality-comparison) with `undefined` while `true` when using the [Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) is due to the willful violation of the specification that explicitly allows that. > > — [“Obsolete features - document.all”](https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-all) at WhatWG - HTML spec > — [“Chapter 4 - ToBoolean - Falsy values”](https://github.com/getify/You-Dont-Know-JS/blob/0d79079b61dad953bbfde817a5893a49f7e889fb/types%20%26%20grammar/ch4.md#falsy-objects) at YDKJS - Types & Grammar ## Minimal value is greater than zero `Number.MIN_VALUE` is the smallest number, which is greater than zero: ```js Number.MIN_VALUE > 0 // -> true ``` ### 💡 Explanation: > `Number.MIN_VALUE` is `5e-324`, i.e. the smallest positive number that can be represented within float precision, i.e. that's as close as you can get to zero. It defines the best resolution that floats can give you. > > Now the overall smallest value is `Number.NEGATIVE_INFINITY` although it's not really numeric in a strict sense. > > — [“Why is `0` less than `Number.MIN_VALUE` in JavaScript?”](https://stackoverflow.com/questions/26614728/why-is-0-less-than-number-min-value-in-javascript) at StackOverflow * [**20.1.2.9** Number.MIN_VALUE](https://www.ecma-international.org/ecma-262/#sec-number.min_value) ## function is not a function > ⚠️ A bug present in V8 v5.5 or lower (Node.js <=7) ⚠️ All of you know about the annoying _undefined is not a function_, but what about this? ```js // Declare a class which extends null class Foo extends null {} // -> [Function: Foo] new Foo instanceof null // > TypeError: function is not a function // > at … … … ``` ### 💡 Explanation: This is not a part of the specification. It's just a bug that has now been fixed, so there shouldn't be a problem with it in the future. ## Adding arrays What if you try to add two arrays? ```js [1, 2, 3] + [4, 5, 6] // -> '1,2,34,5,6' ``` ### 💡 Explanation: The concatenation happens. Step-by-step, it looks like this: ```js [1, 2, 3] + [4, 5, 6] // call toString() [1, 2, 3].toString() + [4, 5, 6].toString() // concatenation '1,2,3' + '4,5,6' // -> '1,2,34,5,6' ``` ## Trailing commas in array You've created an array with 4 empty elements. Despite all, you'll get an array with three elements, because of trailing commas: ```js let a = [,,,] a.length // -> 3 a.toString() // -> ',,' ``` ### 💡 Explanation: > **Trailing commas** (sometimes called "final commas") can be useful when adding new elements, parameters, or properties to JavaScript code. If you want to add a new property, you can simply add a new line without modifying the previously last line if that line already uses a trailing comma. This makes version-control diffs cleaner and editing code might be less troublesome. > > — [Trailing commas](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Trailing_commas) at MDN ## Array equality is a monster Array equality is a monster in JS, as you can see below: ```js [] == '' // -> true [] == 0 // -> true [''] == '' // -> true [0] == 0 // -> true [0] == '' // -> false [''] == 0 // -> true [null] == '' // true [null] == 0 // true [undefined] == '' // true [undefined] == 0 // true [[]] == 0 // true [[]] == '' // true [[[[[[]]]]]] == '' // true [[[[[[]]]]]] == 0 // true [[[[[[ null ]]]]]] == 0 // true [[[[[[ null ]]]]]] == '' // true [[[[[[ undefined ]]]]]] == 0 // true [[[[[[ undefined ]]]]]] == '' // true ``` ### 💡 Explanation: You should watch very carefully for the above examples! The behaviour is described in section [**7.2.13** Abstract Equality Comparison](https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) of the specification. ## `undefined` and `Number` If we don't pass any arguments into the `Number` constructor, we'll get `0`. The value `undefined` is assigned to formal arguments when there are no actual arguments, so you might expect that `Number` without arguments takes `undefined` as a value of its parameter. However, when we pass `undefined`, we will get `NaN`. ```js Number() // -> 0 Number(undefined) // -> NaN ``` ### 💡 Explanation: According to the specification: 1. If no arguments were passed to this function's invocation, let `n` be `+0`. 2. Else, let `n` be ? `ToNumber(value)`. 3. In case of `undefined`, `ToNumber(undefined)` should return `NaN`. Here's the corresponding section: * [**20.1.1** The Number Constructor](https://www.ecma-international.org/ecma-262/#sec-number-constructor) * [**7.1.3** ToNumber(`argument`)](https://www.ecma-international.org/ecma-262/#sec-tonumber) ## `parseInt` is a bad guy `parseInt` is famous by its quirks: ```js parseInt('f*ck'); // -> NaN parseInt('f*ck', 16); // -> 15 ``` **💡 Explanation:** This happens because `parseInt` will continue parsing character-by-character until it hits a character it doesn't know. The `f` in `'f*ck'` is the hexadecimal digit `15`. Parsing `Infinity` to integer is something… ```js // parseInt('Infinity', 10) // -> NaN // ... parseInt('Infinity', 18) // -> NaN... parseInt('Infinity', 19) // -> 18 // ... parseInt('Infinity', 23) // -> 18... parseInt('Infinity', 24) // -> 151176378 // ... parseInt('Infinity', 29) // -> 385849803 parseInt('Infinity', 30) // -> 13693557269 // ... parseInt('Infinity', 34) // -> 28872273981 parseInt('Infinity', 35) // -> 1201203301724 parseInt('Infinity', 36) // -> 1461559270678... parseInt('Infinity', 37) // -> NaN ``` Be careful with parsing `null` too: ```js parseInt(null, 24) // -> 23 ``` **💡 Explanation:** > It's converting `null` to the string `"null"` and trying to convert it. For radixes 0 through 23, there are no numerals it can convert, so it returns NaN. At 24, `"n"`, the 14th letter, is added to the numeral system. At 31, `"u"`, the 21st letter, is added and the entire string can be decoded. At 37 on there is no longer any valid numeral set that can be generated and `NaN` is returned. > > — [“parseInt(null, 24) === 23… wait, what?”](https://stackoverflow.com/questions/6459758/parseintnull-24-23-wait-what) at StackOverflow Don't forget about octals: ```js parseInt('06'); // 6 parseInt('08'); // 8 if support ECMAScript 5 parseInt('08'); // 0 if not support ECMAScript 5 ``` **💡 Explanation:** If the input string begins with "0", radix is eight (octal) or 10 (decimal). Exactly which radix is chosen is implementation-dependent. ECMAScript 5 specifies that 10 (decimal) is used, but not all browsers support this yet. For this reason always specify a radix when using `parseInt`. `parseInt` always convert input to string: ```js parseInt({ toString: () => 2, valueOf: () => 1 }) // -> 2 Number({ toString: () => 2, valueOf: () => 1 }) // -> 1 ``` ## Math with `true` and `false` Let's do some math: ```js true + true // -> 2 (true + true) * (true + true) - true // -> 3 ``` Hmmm… 🤔 ### 💡 Explanation: We can coerce values to numbers with the `Number` constructor. It's quite obvious that `true` will be coerced to `1`: ```js Number(true) // -> 1 ``` The unary plus operator attempts to convert its value into a number. It can convert string representations of integers and floats, as well as the non-string values `true`, `false`, and `null`. If it cannot parse a particular value, it will evaluate to `NaN`. That means we can coerce `true` to `1` easier: ```js +true // -> 1 ``` When you're performing addition or multiplication, the `ToNumber` method is invoked. According to the specification, this method returns: > If `argument` is **true**, return **1**. If `argument` is **false**, return **+0**. That's why we can add boolean values as regular numbers and get correct results. Corresponding sections: * [**12.5.6** Unary `+` Operator](https://www.ecma-international.org/ecma-262/#sec-unary-plus-operator) * [**12.8.3** The Addition Operator (`+`)](https://www.ecma-international.org/ecma-262/#sec-addition-operator-plus) * [**7.1.3** ToNumber(`argument`)](https://www.ecma-international.org/ecma-262/#sec-tonumber) ## HTML comments are valid in JavaScript You will be impressed, but `