Skip to main content

Menu

System onlineAI Tutorials / 2026

AI Tutorials
Knowledge search

Recent · Popular · Tutorials

  • dart3 tutorials
  • java1 tutorial
  • Javascript3 tutorials
  • Node.js Introduction2 tutorials
  • dartdart · General · In depth
  • JavascriptJavascript · Advanced · In depth
  • JavascriptJavascript · Advanced · Mid
  • JavascriptJavascript · Advanced · Short

Find a tutorial

Tell the assistant what you want to learn. It suggests published chapters.

For example: “I want to learn Rust ownership from the start.” The conversation is kept only in this tab.

0 / 2,000 characters

Enter to send, Shift+Enter for a new line. Do not include personal information.

Chapter 2

Operators, Coercion, and Equality

This chapter assumes you already know JavaScript's primitive types and how variables work. The focus is on how the language *behaves* at the edges: what the `+` operator really does, when `==` does something you didn't intend, and how the abstract operations behind coercion actually resolve. The goa
Level: AdvancedLength: Mid2 min read

Read at another depth

Operators, Coercion, and Equality

This chapter assumes you already know JavaScript's primitive types and how variables work. The focus is on how the language behaves at the edges: what the + operator really does, when == does something you didn't intend, and how the abstract operations behind coercion actually resolve. The goal is that you can predict results without running the code.

The `+` Operator's Three Cases

+ is the only arithmetic operator with an overload for strings. The runtime resolves it in this order:

  1. If either operand is an object, convert it to a primitive via ToPrimitive (i.e. valueOf, then toString).
  2. If either primitive is a string, concatenate.
  3. Otherwise, convert both to numbers and add.
Code example in javascript
1 + '2';        // '12' — string branch wins
1 + 2;          // 3
[] + {};        // '[object Object]' — both become primitives first
{} + {};        // NaN in the REPL: leading { parsed as a block, +({}) follows
'5' - 3;        // 2 — subtraction has no string overload, so ToNumber applies
[6] - 1;        // 5 — ToPrimitive gives '6', ToNumber gives 6

Note the asymmetry: + prefers strings, every other arithmetic operator prefers numbers. That asymmetry is where most coercion surprises originate.

Loosely Equal: The `==` Algorithm

== (abstract equality) never compares an object against another object by value, and its coercion rules depend on type pairing. The comparison of '0' == false producing true is not string-to-boolean conversion; it is both sides being converted to numbers.

Code example in javascript
'0' == false;   // true — ToNumber('0') is 0, ToNumber(false) is 0
'' == 0;        // true — same path
null == undefined; // true — special case, nothing else equals null
null == 0;      // false — null only loosely equals undefined
NaN == NaN;     // false — NaN is never equal to itself

For objects, == falls back to reference identity after attempting ToPrimitive on whichever side is an object. That makes [] == '' true ([] primitives to '') but [] == [] false.

When `==` Is Defensible

Most style guides mandate ===. The narrow case where == carries intent: testing against null/undefined in one check.

Code example in javascript
if (value == null) { /* null or undefined */ }

Otherwise use ===. NaN is not equal to anything, including itself, which is why Number.isNaN(x) exists and x === x (a NaN check some libraries use) is a valid idiom.

SameValue and Its Variants

The strict comparison family has three members with different treatment of edge values:

OperationNaN`+0` vs `-0`Objects compared by
`===`falsetruereference
`Object.is`truefalsereference
`Object.assign` semanticsn/an/acopies enumerable own props

Object.is is what Array.prototype.includes uses internally, which is why [NaN].includes(NaN) is true but [NaN].indexOf(NaN) is -1 — indexOf uses ===. This is a real-world gotcha, not a spec curiosity.

Code example in javascript
[-0, +0].includes(-0);   // true
Object.is(-0, +0);       // false
Object.is(NaN, NaN);     // true

ToPrimitive and Symbol.toPrimitive

When an object is coerced, the engine checks in order: the well-known Symbol.toPrimitive method, then valueOf, then toString (with a hint of "string", "number", or "default"). You can and sometimes should control this deliberately:

Code example in javascript
const temp = {
  [Symbol.toPrimitive](hint) {
    return hint === 'string' ? '20°C' : 20;
  }
};
`${temp}`;       // '20°C'
temp + 5;        // 25
temp == 20;      // true

A valueOf that returns an object (rather than a primitive) is silently ignored and the next method is tried — a common source of "why did my custom valueOf do nothing" bugs.

Comparison Operators

<, >, <=, >= also run ToPrimitive on both sides, but there is no string-preferring overload for numeric comparison: if both primitives are strings, they compare lexicographically; otherwise numerically.

Code example in javascript
'10' < '9';   // true — string comparison, '1' < '9'
'10' < 9;     // false — numeric comparison

Mixed-type sorts in Array.prototype.sort without a comparator exploit exactly this behavior and produce nondeterministic-looking output across engines. Always pass a comparator.

Summary

  • + prefers strings; every other arithmetic operator prefers numbers.
  • == coerces via type-pairing rules; === compares identity; Object.is treats NaN and ±0 distinctly.
  • Objects coerce through Symbol.toPrimitive, valueOf, then toString, with a hint.
  • Prefer explicit conversions (Number(x), String(x)) at boundaries; reserve == for the null check.

Exercise

What does [] + [] evaluate to, and which branch of the + algorithm resolves it?

Show answer

'' — the empty string. Both arrays convert to primitives via ToPrimitive, yielding '' each; since either operand is a string, the concatenation branch applies, and '' + '' is ''.

Exercise

Why does [NaN].includes(NaN) return true while [NaN].indexOf(NaN) returns -1?

Show answer

includes uses the SameValueZero comparison (NaN equals NaN), whereas indexOf uses strict ===, under which NaN is never equal to anything, including itself.

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Operators, Coercion, and Equality

0 / 2,000 characters

Plain text, for example: the second code sample does not compile. Do not include personal information.

You can't do that