JavaScript Gotchas

Sections:

Comparison with ==

Rules: Equality (==) - JavaScript | MDN Equality comparisons and sameness - JavaScript | MDN Some key rules: if both objects: return true if objects are the same object if both numbers: 0 and -0 are considered equal if both numbers: comparing anything with NaN (including NaN == NaN) returns false null/undefined v. null/undefined [output: true] [e.g. null == undefined returns true] [note: null === undefined returns false] null/undefined v. other [output: false] symbol v. non-symbol [output: false] object v. non-object: convert object to primitive (this is typically but not always a string) bool v. non-bool: convert bool to number string v. bigint/bool/number: convert string to number Some common comparisons:

COMPARISON WITH ==: NULL/UNDEFINED
null v. undefined [output: true]

COMPARISON WITH ==: NUMBERS
number v. array/date/function/object [object to primitive (string) to num]
number v. bigint [compare mathematical values]
number v. boolean [bool to num]
number v. null/undefined [output: false]
number v. string [string to num]
number v. symbol [output: false]

COMPARISON WITH ==: STRINGS
string v. array/date/function/object [object to primitive (string)]
string v. bigint [string to num, compare mathematical values]
string v. boolean [bool to num, string to num]
string v. null/undefined [output: false]
string v. number [string to num]
string v. symbol [output: false]

COMPARISON WITH ==: BOOLEANS
boolean v. array/date/function/object [object to primitive (string), bool to num, string to num]
boolean v. bigint [bool to num, compare mathematical values]
boolean v. null/undefined [output: false]
boolean v. number [bool to num]
boolean v. string [bool to num, string to num]
boolean v. symbol [output: false]

Addition/Concatenation with Binary +

Rules: Addition (+) - JavaScript | MDN [addition and concatenation] Some key rules: objects are coerced to primitives if at least one side is a symbol: throws if at least one side is a string: concatenation if both bigints: addition if one bigint: throws else converted to numbers: addition Some common comparisons:

ADDITION/CONCATENATION WITH BINARY +: NUMBERS
number + array/date/function/object [object to primitive (string), num to string] [CONCATENATION]
number + bigint [THROWS]
number + boolean [bool to num] [ADDITION]
number + null/undefined [null/undefined to num] [ADDITION]
number + number [ADDITION]
number + string [num to string] [CONCATENATION]
number + symbol [THROWS]

ADDITION/CONCATENATION WITH BINARY +: STRINGS
string + array/date/function/object [object to primitive (string)] [CONCATENATION]
string + bigint [CONCATENATION]
string + boolean [bool to string] [CONCATENATION]
string + null/undefined [null/undefined to string] [CONCATENATION]
string + number [num to string] [CONCATENATION]
string + string [CONCATENATION]
string + symbol [THROWS]

ADDITION/CONCATENATION WITH BINARY +: BOOLEANS
boolean + array/date/function/object [object to primitive (string), bool to string] [CONCATENATION]
boolean + bigint [THROWS]
boolean + boolean [bool to num (for both)] [ADDITION]
boolean + null/undefined [bool to num, null/undefined to num] [ADDITION]
boolean + number [bool to num] [ADDITION]
boolean + string [bool to string] [CONCATENATION]
boolean + symbol [THROWS]

Type Coercion with Number() / Addition with Unary +

Rules: Unary plus (+) - JavaScript | MDN Number - JavaScript | MDN See also: Number.parseFloat() - JavaScript | MDN Number.parseInt() - JavaScript | MDN Some key rules: undefined -> NaN null -> 0 boolean: true -> 1 boolean: false -> 0 strings: 'Leading and trailing whitespace/line terminators are ignored.' strings: 'A leading 0 digit does not cause the number to become an octal literal (or get rejected in strict mode).' strings: e.g. "010" -> 10 (not 8) strings: e.g. "018" -> 18 strings: '+ and - are allowed at the start of the string to indicate its sign.' 'However, the sign can only appear once, and must not be followed by whitespace.' strings: "Infinity" -> Infinity strings: "-Infinity" -> -Infinity strings: "" -> 0 strings: Numeric separators are not allowed. bigints: +vBigInt throws, Number(vBigInt) works but can lose precision symbols: throw objects: converted to a primitive, typically a string, the primitive is converted to a number Number(x) and +x are almost always equivalent. BigInt handling is an exception. Some key examples:

COERCION WITH NUMBER() / ADDITION WITH UNARY +: PRIMITIVES
bigint: 0n: +0n throws, Number(0n) -> 0
bigint: 1n: +1n throws, Number(1n) -> 1
boolean: false -> 0
boolean: true -> 1
null/undefined: null -> 0
null/undefined: undefined -> NaN
number: -0.0 -> -0.0 [note: '+-0.0 = -0.0' (unchanged), whereas '0 + -0.0 = 0.0']
number: 0.0 -> 0.0
number: Infinity -> Infinity
number: NaN -> NaN
string: "" -> 0
string: " " -> 0
string: "0" -> 0
string: "010" -> 10
string: "0abc" -> NaN
string: "0b10" -> 2
string: "0o10" -> 8
string: "0x0" -> 0
string: "0x10" -> 16
string: "123abc" -> NaN
string: "abc" -> NaN [the same applies to "Inf"/"Infin"/"infinity"]
string: "false" -> NaN
string: "Infinity" -> Infinity
string: "NaN" -> NaN
string: "true" -> NaN
symbol: throws

COERCION WITH NUMBER() / ADDITION WITH UNARY +: OBJECTS
array: [] -> "" -> 0
array: [" "] -> " " -> 0
array: ["0"] -> "0" -> 0
array: ["1"] -> "1" -> 1
array: ["abc"] -> "abc" -> NaN
array: [0] -> "0" -> 0
array: [1] -> "1" -> 1
array: [1, 2] -> "1,2" -> NaN
date: new Date() -> new Date().valueOf()
date: new Date(Date.UTC(2000)) -> new Date(Date.UTC(2000)).valueOf()
function: _=>0 -> NaN [e.g. +(_=>0) returns NaN]
function: ()=>{} -> NaN [e.g. +(()=>{}) returns NaN]
object: {} -> "[object Object]" -> NaN

Type Coercion with Boolean() / !!

Rules: Boolean - JavaScript | MDN Some key rules: null/undefined -> false numbers: 0/-0/NaN -> false (other numbers -> true) bigints: 0n -> false (other bigints -> true) strings: "" -> false (other strings -> true) symbols: true objects: true 'there are only a handful of values that get coerced to false — these are called falsy values. All other values are called truthy values.' 'Note that truthiness is not the same as being loosely equal to true or false.' Boolean(x) and !!x and 'x ? true : false' are equivalent. Some key examples:

COERCION WITH BOOLEAN() / !! / ? : : PRIMITIVES
bigint: 0n -> false [all bigints truthy except 0n]
bigint: 1n -> true
boolean: false: unchanged
boolean: true: unchanged
null/undefined: null -> false
null/undefined: undefined -> false
number: -0.0 -> false
number: 0.0 -> false
number: Infinity -> true
number: -0 -> false
number: 0 -> false
number: 1 -> true
number: NaN -> false [all numbers truthy except 0/-0/NaN]
objects: (all): true
string: "" -> false [all strings truthy except ""]
string: " " -> true
string: "0" -> true
string: "010" -> true
string: "0abc" -> true
string: "0b10" -> true
string: "0o10" -> true
string: "0x0" -> true
string: "0x10" -> true
string: "123abc" -> true
string: "abc" -> true
string: "false" -> true
string: "Infinity" -> true
string: "NaN" -> true
string: "true" -> true
symbol: true

COERCION WITH BOOLEAN() / !! / ? : : OBJECTS
(note: all objects return true)

Type Coercion with String()

Rules: String - JavaScript | MDN Some key rules: undefined -> "undefined" null -> "null" boolean: true -> "true" boolean: false -> "false" numbers: 'converted with the same algorithm as toString(10)' bigints: 'converted with the same algorithm as toString(10)' symbols: throw objects: converted to a primitive, typically a string, the primitive is converted to a string Some key examples:

COERCION WITH STRING(): PRIMITIVES
bigint: 0n -> "0"
bigint: 1n -> "1"
boolean: false: "false"
boolean: true: "true"
null/undefined: null -> "null"
null/undefined: undefined -> "undefined"
number: -0.0 -> "0"
number: 0.0 -> "0"
number: Infinity -> "Infinity"
number: NaN -> "NaN"
string: unchanged
symbol: throws

COERCION WITH STRING(): OBJECTS
array: [] -> ""
array: [" "] -> " "
array: ["0"] -> "0"
array: ["1"] -> "1"
array: ["abc"] -> "abc"
array: [0] -> "0"
array: [1] -> "1"
array: [1, 2] -> "1,2"
date: new Date() -> (current date as string)
date: new Date(Date.UTC(2000)) -> "Sat Jan 01 2000 00:00:00 GMT+0000 (Coordinated Universal Time)"
function: _=>0 -> "_=>0"
function: ()=>{} -> "()=>{}"
object: {} -> "[object Object]"

isNaN/Number.isNaN and isFinite/Number.isFinite

The following functions/methods have versions in the global object, and versions in the Number object: isNaN() isFinite() parseInt() parseFloat() The differences are summarised within these links: Number.isNaN() - JavaScript | MDN Number.isFinite() - JavaScript | MDN Number.parseInt() - JavaScript | MDN Number.parseFloat() - JavaScript | MDN Some details: isNaN() coerces to number then checks for NaN. Number.isNaN() doesn't coerce. isFinite() coerces to number then checks for finiteness. Number.isFinite() doesn't coerce. parseInt() is identical to Number.parseInt(). parseFloat() is identical to Number.parseFloat(). Number.isNaN(x) is equivalent to: (typeof x == "number") && isNaN(x) and equivalent to: x !== x Number.isFinite(x) is equivalent to: (typeof x == "number") && isFinite(x)

Array key get value

Some examples: console.log([123][0]); //123 console.log([123][00]); //123 console.log([123][0x0]); //123 console.log([123][""]); //undefined console.log([123]["0"]); //123 console.log([123]["00"]); //undefined console.log([123]["0x0"]); //undefined console.log([123][[]]); //undefined console.log([123][{}]); //undefined

JSON

Some examples: //value to JSON string: console.log(JSON.stringify(Infinity)); //null console.log(JSON.stringify(-Infinity)); //null console.log(JSON.stringify(NaN)); //null console.log(JSON.stringify(null)); //null console.log(JSON.stringify(undefined)); //undefined console.log(JSON.stringify(-0.0)); //0 (sign is lost) console.log(JSON.stringify(true)); //true console.log(JSON.stringify(false)); //false //JSON string to value: //console.log(JSON.parse("Infinity")); //error //console.log(JSON.parse("-Infinity")); //error //console.log(JSON.parse("NaN")); //error console.log(JSON.parse("null")); //null //console.log(JSON.parse("undefined")); //error console.log(JSON.parse("-0.0")); //-0 console.log(JSON.parse("true")); //true console.log(JSON.parse("false")); //false

Obfuscated code example

Test your JavaScript skills on this obfuscated code example: console.log((![] + [])[+[]] + (![] + [])[+!+[]] + ([![]] + [][[]])[+!+[] + [+[]]] + (![] + [])[!+[] + !+[]]); Source: TikTok just released its React Native killer… - YouTube

Operators: Equality (==) - JavaScript | MDN Addition (+) - JavaScript | MDN [addition and concatenation] Unary plus (+) - JavaScript | MDN Type coercion: Boolean - JavaScript | MDN Number - JavaScript | MDN String - JavaScript | MDN Primitive types and object-to-primitive coercion: Primitive - Glossary | MDN JavaScript data types and data structures - JavaScript | MDN Symbol.toPrimitive - JavaScript | MDN Equality comparisons and sameness - JavaScript | MDN Further links: Number.parseFloat() - JavaScript | MDN Number.parseInt() - JavaScript | MDN JavaScript quiz videos by Conner Ardman: [duration: 11:49][date: 2024-03-25] ex-FAANG Developer vs "Impossible" JavaScript Quiz - YouTube [duration: 10:01][date: 2023-07-17] HARD JavaScript Challenges | Do You REALLY Know JS? - YouTube [duration: 12:33][date: 2024-06-08] ex-FAANG Developer vs "Hardest" JavaScript Quiz - YouTube