Photo by Eric Er on Unsplash

On this page

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"); // 42
String(42); // "42"
Boolean(0); // false
BigInt("10"); // 10n

Number 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 === (except NaN and -0/+0 details).
  • null == undefined is true; null compared 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 0
true == 1; // true
null == undefined; // true
"" == 0; // true

The 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 number
true === 1; // false — boolean versus number
0 === -0; // true — same number value
Object.is(0, -0); // false — distinguishes -0
Object.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 only value == null to catch both null and undefined).
  • 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.

Search articles

Type a word to search published articles.

Open the search page