Welcome to the weird parts. Let’s start with the most infamous party trick.

console.log(isNaN(NaN)); // true // But wait... console.log(isNaN("hello")); // true (because "hello" can't be a number) That’s why ES6 gave us Number.isNaN() which actually behaves. In many languages, if you forget to declare a variable, you get an error. In JavaScript (non-strict mode), you get a present :

function oops() { accidentalGlobal = "I'm everywhere now"; } oops(); console.log(window.accidentalGlobal); // "I'm everywhere now" You didn't use var , let , or const ? Congratulations, you just polluted the global object. This is why we have linters and "use strict" . The this keyword is like a chameleon on caffeine. Its value depends entirely on how a function is called.

const bound = showThis.bind("hello"); bound(); // String {"hello"}

console.log([] + []); // "" (empty string) console.log([] + {}); // "[object Object]" console.log({} + []); // 0 (wait, WHAT?) The last one is a parsing quirk. In some engines, {} at the start of a line is treated as an empty block, not an object. So {} + [] becomes + [] which coerces to 0. Never, ever trust == . It’s like asking a toddler if two things are the same.

const arr = [1, 2, 3]; arr["foo"] = "bar"; console.log(arr); // [1, 2, 3, foo: "bar"] console.log(arr.length); // 3 (still!) The length property only counts numeric indices. The string key "foo" is there, but the array pretends it isn’t. JavaScript tries to be "helpful" with Automatic Semicolon Insertion (ASI). But sometimes, it helps too much.