Beginner

Operators in JavaScript

Master JavaScript operators including arithmetic, comparison, logical, assignment, and the language-specific quirks of type coercion

Operators are the building blocks of expressions in JavaScript. They let you do arithmetic, compare values, combine boolean conditions, and assemble strings. As a dynamically and weakly typed language, JavaScript brings some unique behavior to operators — most notably type coercion, where the language quietly converts values between types when you mix them in an expression.

JavaScript’s multi-paradigm nature means operators show up in surprising places: arrow functions use =>, the spread operator (...) unpacks iterables, and short-circuit evaluation (&&, ||, ??) is routinely used as control flow. In this tutorial we’ll work through the everyday operators, then look at the famous quirks that make JavaScript both powerful and occasionally bewildering.

By the end you’ll know how to perform calculations, compare values safely with ===, build strings with template literals, and recognize the coercion gotchas that trip up newcomers.

Arithmetic, Comparison, and Logical Operators

Create a file named operators.js:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Arithmetic operators
const a = 17;
const b = 5;

console.log("a + b =", a + b);       // addition
console.log("a - b =", a - b);       // subtraction
console.log("a * b =", a * b);       // multiplication
console.log("a / b =", a / b);       // division (always float)
console.log("a % b =", a % b);       // remainder
console.log("a ** b =", a ** b);     // exponentiation (ES2016)

// Integer-style division requires Math.trunc or Math.floor
console.log("Math.trunc(a / b) =", Math.trunc(a / b));

// Unary operators
let n = 10;
console.log("-n =", -n);
console.log("++n =", ++n);           // pre-increment
console.log("n-- =", n--);           // post-decrement (returns 11, then n=10)
console.log("n =", n);

// Comparison operators
console.log("a > b:", a > b);
console.log("a === 17:", a === 17);  // strict equality (no coercion)
console.log("a !== b:", a !== b);

// Logical operators (short-circuit)
const isAdult = true;
const hasTicket = false;
console.log("isAdult && hasTicket:", isAdult && hasTicket);
console.log("isAdult || hasTicket:", isAdult || hasTicket);
console.log("!isAdult:", !isAdult);

Notice that / always produces a floating-point result — there is no separate integer division operator. To truncate toward zero, use Math.trunc. Logical operators short-circuit: && stops at the first falsy value, || stops at the first truthy value, and both return the value that stopped them, not necessarily a boolean.

Strict vs Loose Equality and Type Coercion

JavaScript’s weak typing makes equality the most error-prone operator in the language. The rule of thumb: always prefer === over ==.

Create a file named equality.js:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Loose equality (==) performs type coercion
console.log('"5" == 5:', "5" == 5);           // true  — string coerced to number
console.log('0 == false:', 0 == false);       // true  — boolean coerced to number
console.log('null == undefined:', null == undefined); // true — special case
console.log('"" == 0:', "" == 0);             // true  — empty string -> 0

// Strict equality (===) checks type AND value
console.log('"5" === 5:', "5" === 5);         // false
console.log('0 === false:', 0 === false);     // false
console.log('null === undefined:', null === undefined); // false

// Coercion in arithmetic context
console.log('"3" + 4:', "3" + 4);             // "34"  — + with string concatenates
console.log('"3" - 4:', "3" - 4);             // -1    — - forces numeric coercion
console.log('"3" * "4":', "3" * "4");         // 12    — both coerced to numbers

// Nullish coalescing (??) vs logical OR (||)
const count = 0;
console.log("count || 10:", count || 10);     // 10  — 0 is falsy
console.log("count ?? 10:", count ?? 10);     // 0   — only null/undefined trigger ??

The + operator is the famous trouble-maker: with any string operand it becomes string concatenation, while every other arithmetic operator forces numeric coercion. The nullish coalescing operator ?? (ES2020) was added specifically to avoid the || trap where valid falsy values like 0 or "" get replaced by defaults.

Assignment, String, and Modern Operators

Create a file named modern_ops.js:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Compound assignment
let score = 10;
score += 5;   console.log("after +=:", score);   // 15
score -= 3;   console.log("after -=:", score);   // 12
score *= 2;   console.log("after *=:", score);   // 24
score /= 4;   console.log("after /=:", score);   // 6
score **= 2;  console.log("after **=:", score);  // 36

// Logical assignment (ES2021)
let cached = null;
cached ??= "default";   // assign only if null/undefined
console.log("cached:", cached);

// String concatenation with + and template literals
const name = "Ada";
const year = 1843;
const oldStyle = "In " + year + ", " + name + " wrote the first algorithm.";
const modern   = `In ${year}, ${name} wrote the first algorithm.`;
console.log(oldStyle);
console.log(modern);

// Ternary (conditional) operator
const age = 17;
const status = age >= 18 ? "adult" : "minor";
console.log("status:", status);

// Spread operator (...) in arrays
const head = [1, 2];
const tail = [3, 4, 5];
const combined = [...head, ...tail];
console.log("combined:", combined);

// Optional chaining (?.) — short-circuits on null/undefined
const user = { profile: { name: "Grace" } };
console.log("user?.profile?.name:", user?.profile?.name);
console.log("user?.address?.city:", user?.address?.city);

// Operator precedence: ** binds tighter than *, * tighter than +
console.log("2 + 3 * 4 ** 2 =", 2 + 3 * 4 ** 2);  // 2 + 3*16 = 50

Template literals (backtick strings) are the modern replacement for + concatenation — they’re more readable and support multi-line strings without escape characters. Optional chaining (?.) and nullish coalescing (??) work together as a safer way to navigate possibly-missing object properties.

Running with Docker

1
2
3
4
5
6
7
# Pull the official Node.js Alpine image
docker pull node:22-alpine

# Run each example
docker run --rm -v $(pwd):/app -w /app node:22-alpine node operators.js
docker run --rm -v $(pwd):/app -w /app node:22-alpine node equality.js
docker run --rm -v $(pwd):/app -w /app node:22-alpine node modern_ops.js

Expected Output

Output from operators.js:

a + b = 22
a - b = 12
a * b = 85
a / b = 3.4
a % b = 2
a ** b = 1419857
Math.trunc(a / b) = 3
-n = -10
++n = 11
n-- = 11
n = 10
a > b: true
a === 17: true
a !== b: true
isAdult && hasTicket: false
isAdult || hasTicket: true
!isAdult: false

Output from equality.js:

"5" == 5: true
0 == false: true
null == undefined: true
"" == 0: true
"5" === 5: false
0 === false: false
null === undefined: false
"3" + 4: 34
"3" - 4: -1
"3" * "4": 12
count || 10: 10
count ?? 10: 0

Output from modern_ops.js:

after +=: 15
after -=: 12
after *=: 24
after /=: 6
after **=: 36
cached: default
In 1843, Ada wrote the first algorithm.
In 1843, Ada wrote the first algorithm.
status: minor
combined: [ 1, 2, 3, 4, 5 ]
user?.profile?.name: Grace
user?.address?.city: undefined
2 + 3 * 4 ** 2 = 50

Key Concepts

  • / always returns a float — JavaScript has only one number type (64-bit float); use Math.trunc or Math.floor for integer division
  • === is almost always what you want — strict equality compares without type coercion; reserve == only when you specifically need the null == undefined shortcut
  • + is overloaded — with any string operand it concatenates, otherwise it adds; other arithmetic operators force numeric coercion
  • Logical operators return values, not booleansa || b returns a if truthy, otherwise b; this enables short-circuit defaulting
  • ?? distinguishes “missing” from “falsy” — unlike ||, the nullish coalescing operator only falls through on null/undefined, preserving 0, "", and false
  • Template literals replace string concatenation — backticks with ${expr} interpolation are clearer than chained + operators
  • Optional chaining (?.) safely accesses properties on possibly-null objects, returning undefined instead of throwing
  • Precedence matters: ** > * / % > + - > comparisons > && > || ?? — when in doubt, parenthesize

Running Today

All examples can be run using Docker:

docker pull node:22-alpine
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining