This chapter follows Pass by value and reference in JavaScript.
JavaScript rarely refuses an operation because types differ. Instead it coerces — converts values toward the type an operator or function expects. Some coercions are explicit (Number("3")). Others are implicit ("3" - 1). Equality has two operators: == applies coercion; === does not.
Explicit conversion
Prefer making conversions visible on the line where you need them:
Number("42"); // 42String(42); // "42"Boolean(0); // falseBigInt("10"); // 10nNumber on an empty string is 0. On non-numeric strings it is NaN. parseInt and parseFloat parse prefixes and ignore trailing junk, which is useful for user input but easy to misuse on full strings.
Abstract operations behind operators
The spec names algorithms such as ToNumber, ToString, and ToBoolean. You do not call them directly; +, -, ==, and if do.
Unary + is ToNumber. Unary ! is ToBoolean followed by logical NOT. The + binary operator uses ToPrimitive on both sides first. If either result is a string, the operator concatenates. Otherwise both sides become numbers and add.
That single rule explains "9" + 1 === "91" and "9" - 1 === 8.
Loose equality (==)
== compares after applying the IsLooselyEqual algorithm. Rough patterns:
- If types match, behave like
===(exceptNaNand-0/+0details). null == undefinedis true;nullcompared to anything else (except undefined) is false.- If one side is number and the other string, convert the string to number.
- If one side is boolean, convert boolean to number (
true→1,false→0) and compare again. - If one side is object, run ToPrimitive on the object and compare again.
[] == 0; // true — "" then 0true == 1; // truenull == undefined; // true"" == 0; // trueThe chain is deterministic but hard to memorize. That is why application code usually avoids ==.
Strict equality (===)
=== compares without conversion. Types must match and values must match.
[] === 0; // false — object versus numbertrue === 1; // false — boolean versus number0 === -0; // true — same number valueObject.is(0, -0); // false — distinguishes -0Object.is(NaN, NaN); // true — unlike ===Use === when the type is part of the meaning. Use Object.is when you care about NaN or -0 specifically.
Truthiness and falsiness
In if, &&, ||, and the ternary, the condition is evaluated with ToBoolean. Falsy values are false, 0, -0, 0n, "", null, undefined, and NaN. Everything else is truthy, including [], {}, and "0".
Truthy and falsy describe how a value converts in boolean context. They are not the same as equality to true or false:
if ("0") { // runs — non-empty string is truthy}SameValue and SameValueZero
Internal algorithms use SameValueZero for Array.prototype.includes and Set membership: NaN equals NaN, and -0 equals +0. Object.is implements SameValue, which treats -0 and +0 as different.
When you define custom collections or compare cache keys, pick the same semantics deliberately instead of mixing === and includes on the same data.
Practical defaults
- Compare with
===unless you have a documented reason for==(often onlyvalue == nullto catch bothnullandundefined). - Convert at the boundary: parse JSON numbers, validate enums, then work with one type inside the module.
- For deep structural equality, use a test matcher or a small library;
===on objects compares references, not shape.
JavaScript Fails applies these rules to famous one-liners. Numbers and BigInt covers numeric conversion and safe integers.
Next: Numbers and BigInt.