1. Arrays and Equality
[] == ![]; // -> true
// Explanation: Arrays are truthy, so ![] is false, which coerces to 0. [] coerces to 0, so 0 == 0 is true.
true == []; // -> false
true == ![]; // -> false
// Explanation: true converts to 1 and [] converts to 0. 1 != 0.
false == []; // -> true
false == ![]; // -> true
// Explanation: false and [] both convert to 0. 0 == 0.
2. Type Coercion Oddities
!!"false" == !!"true"; // -> true
// Explanation: Both strings are truthy, so !! converts them to true.
"b" + "a" + +"a" + "a"; // -> 'baNaNa'
// Explanation: +"a" converts 'a' to NaN, so it becomes "ba" + NaN + "a" which is 'baNaNa'.
NaN === NaN; // -> false
// Explanation: NaN is not equal to anything, including itself.
3. Object Comparison
Object.is(NaN, NaN); // -> true
NaN === NaN; // -> false
// Explanation: Object.is and === have different behaviors for NaN.
Object.is(-0, 0); // -> false
-0 === 0; // -> true
// Explanation: -0 and 0 are considered equal with === but not with Object.is.
4. Fun with Syntax
[1, 2, 3] + [4, 5, 6]; // -> '1,2,34,5,6'
// Explanation: Arrays are converted to strings and concatenated.
let a = [, , ,];
a.length; // -> 3
// Explanation: Trailing commas create empty slots, affecting array length.
5. Number Coercion and Parsing
parseInt(null, 24); // -> 23
// Explanation: null is converted to a string and then parsed according to the specified radix.
0.1 + 0.2; // -> 0.30000000000000004
// Explanation: Floating-point arithmetic can produce imprecise results.
true + true; // -> 2
(true + true) * true - true; // -> 1
// Explanation: Booleans are coerced to numbers in arithmetic operations.
Number(); // -> 0
Number(undefined); // -> NaN
// Explanation: Number without arguments returns 0, with undefined returns NaN.
6. Unexpected Typeof and Instanceof
typeof NaN; // -> 'number'
// Explanation: Despite its name, NaN is of type 'number'.
typeof null; // -> 'object'
// Explanation: Null is considered an object in JavaScript, although it is a primitive value.
7. Miscellaneous
{} + []; // -> 0
[] + {}; // -> '[object Object]'
// Explanation: The order of operations and type coercion produce different results.
[10, 1, 3].sort(); // -> [1, 10, 3]
// Explanation: Default sorting converts elements to strings, sorting them lexicographically.
let f = () => {};
f(); // -> undefined
// Explanation: Arrow function with empty block returns undefined.
let f = function() { return arguments; };
f("a"); // -> { '0': 'a' }
// Explanation: Regular function captures arguments.
let f = () => arguments;
f("a"); // -> ReferenceError: arguments is not defined
// Explanation: Arrow function does not capture arguments.
(() => {
try {
return 2;
} finally {
return 3;
}
})(); // -> 3
// Explanation: finally block overrides the return statement.
new class F extends (String, Array) {}(); // -> F []
// Explanation: Extends clause uses the last argument, so class extends Array.
let x, { x: y = 1 } = { x };
y; // -> 1
// Explanation: Destructuring with default value when x is undefined.
[...[..."..."]].length; // -> 3
// Explanation: Spreading a string spreads its characters into an array.
foo: {
console.log("first");
break foo;
console.log("second");
}
// Explanation: Labeled block with break statement.
typeof new class { class() {} }(); // -> 'object'
// Explanation: Keyword can be used as method name.
📚 Other resources
qit.tools - JavaScript's Gotchas (WTF JS): Unexpected Behaviors
Top comments (1)
Well, can't get anything better than that can ya JavaScript? No sir can't. This is that one language that works as the HR under the hood!