Knowledge Base/concepts/JavaScript Operators by Intent

JavaScript Operators by Intent

JavaScript operators are easier to learn when you stop sorting them by symbol and start sorting them by intent. +, ===, &&, ??, ? :, and += are not one big drawer of punctuation. Each one answers a different kind of question: calculate a value, compare two values, choose a fallback, combine conditions, update state, or inspect what something is.

The bug usually starts when an operator is technically valid but says the wrong thing.

Table of contents

Choose the operator by the job

When you read an expression, ask what job the operator is doing:

IntentCommon operatorsResult shape
Calculate a number+, -, *, /, %, **Number
Join text+String
Compare values===, !==, <, >, <=, >=Boolean
Combine conditions&&, `
Choose between two valuescondition ? a : bOne of two values
Provide a fallback??, `
Update a variable=, +=, -=, *=, /=, ++, --Assigned or updated value
Inspect a valuetypeof, in, instanceofUsually boolean or type string
Safely access a path?.Property value or undefined

That table is more useful than memorizing a symbol list. Operators are tiny, but they carry meaning. Good code makes that meaning obvious.

Calculate a value

Arithmetic operators create numbers from other numbers:

const subtotal = 24 * 3;
const shipping = 6;
const total = subtotal + shipping;

console.log(total); // 78

% gives the remainder after division:

const queuePosition = 18;
const isEvenPosition = queuePosition % 2 === 0;

console.log(isEvenPosition); // true

** raises a number to a power:

const area = 5 ** 2;

console.log(area); // 25

Arithmetic intent is simple: the expression should describe a calculation. If the code is really asking a yes-or-no question, add a comparison and make the boolean explicit.

Join text deliberately

The + operator has two jobs in JavaScript. With numbers, it adds. With strings, it concatenates.

console.log(5 + 3); // 8
console.log("5" + 3); // "53"

That second line is not operator precedence being weird. It is JavaScript choosing string concatenation because one side is already a string.

The naive version looks harmless:

const cartCount = "5";
const bonusItems = 3;

const totalItems = cartCount + bonusItems;

console.log(totalItems); // "53"

If the intent is numeric addition, convert the value first:

const totalItems = Number(cartCount) + bonusItems;

console.log(totalItems); // 8

If the intent is text, template literals usually read better:

const label = `Items: ${totalItems}`;

For string interpolation details, see Template Literals.

Compare values

Comparison operators answer yes-or-no questions.

const age = 16;

const isTeen = age >= 13 && age < 20;

console.log(isTeen); // true

Use strict equality by default:

console.log(5 === 5); // true
console.log("5" === 5); // false

Loose equality can coerce values before comparing them:

console.log("5" == 5); // true

That is legal JavaScript, but it asks the reader to track hidden conversion rules. Most of the time, the clearer fix is to convert deliberately, then compare strictly:

const typedAge = "18";
const canVote = Number(typedAge) >= 18;

console.log(canVote); // true

Comparisons should read like rules. If the rule has a boundary, include it intentionally with >= or <= instead of hoping the next person guesses the edge case.

Combine conditions

Logical operators combine decisions:

const age = 16;
const hasGuardianApproval = true;

const canAccessWorkshop = age >= 13 && (age >= 18 || hasGuardianApproval);

console.log(canAccessWorkshop); // true

&& means every required condition must pass. || means at least one option may pass. ! flips a boolean:

const isBlocked = false;
const canContinue = !isBlocked;

console.log(canContinue); // true

The trap is that && and || return original values, not guaranteed booleans:

console.log("Ada" && "active"); // "active"
console.log("" || "Guest"); // "Guest"

That behavior is useful for fallbacks and guards, but it can surprise you if you expected a literal true or false. The Truthy and Falsy Values guide covers the coercion rules under these operators.

Choose one value

Use the ternary operator when one condition chooses between two values:

const isPublished = false;
const statusLabel = isPublished ? "Published" : "Draft";

console.log(statusLabel); // "Draft"

That is the right intent: one condition, two possible values.

This is the wrong shape:

isPublished ? publishPost() : saveDraft();

That code performs actions. An if statement says that more clearly:

if (isPublished) {
  publishPost();
} else {
  saveDraft();
}

The JavaScript Ternary Operator article goes deeper on where this operator helps and where it turns smug.

Provide a fallback

Fallback operators answer a different question: "If this value is missing or unusable, what should I use instead?"

|| falls back on any falsy value:

const savedPageSize = 0;
const pageSize = savedPageSize || 20;

console.log(pageSize); // 20

That is correct only if 0 should be treated as unusable.

?? falls back only on null or undefined:

const savedPageSize = 0;
const pageSize = savedPageSize ?? 20;

console.log(pageSize); // 0

Use ?? when 0, "", or false are valid values. Use || when any falsy value should trigger the fallback. That one distinction prevents a surprising number of tiny production bugs.

Update existing state

Assignment operators change a variable:

let balance = 200;

balance += 45;
balance -= 70;
balance -= 5;

console.log(balance); // 170

The long form is equivalent:

balance = balance - 5;

Shorthand assignment is best when the intent is "move this value forward from where it is now." Scores, balances, counters, totals, and loop indexes are common examples.

++ and -- are even narrower: update by exactly one.

let count = 4;

const first = count++;
const second = ++count;

console.log(first); // 4
console.log(second); // 6
console.log(count); // 6

Postfix count++ gives back the old value, then updates. Prefix ++count updates first, then gives back the new value. If that distinction matters to the surrounding expression, consider splitting the update onto its own line. Your future self has enough to do.

Inspect a value

Some operators tell you about a value instead of transforming it:

console.log(typeof "Ada"); // "string"
console.log(typeof 42); // "number"

Use in to check whether a property exists on an object or its prototype chain:

const user = { name: "Ada", role: "admin" };

console.log("role" in user); // true

Use instanceof when you need to check whether an object comes from a constructor:

const createdAt = new Date();

console.log(createdAt instanceof Date); // true

These operators are not about display or calculation. They are about type, shape, and object relationships.

Access values safely

Optional chaining, ?., accesses a property only when the value before it is not null or undefined:

const user = {
  profile: {
    city: "London",
  },
};

console.log(user.profile?.city); // "London"
console.log(user.settings?.theme); // undefined

Use it when a path is genuinely optional. Do not use it as a blanket way to hide broken assumptions.

const button = document.querySelector("#save");

button?.addEventListener("click", save);

That is fine if the save button is optional on this page. If the page cannot work without it, a clear failure during development is better than a silent missing click handler.

Common operator mixups

The operator is often valid. The intent is wrong.

MistakeWhy it failsBetter shape
`count10when0` is valid
value == "5"Allows hidden coercionConvert, then use ===
isReady ? start() : stop()Ternary hides actionsUse if
items && items.lengthReturns items when falsy, not a booleanitems.length > 0 when items must be an array
score = score + 1 > maxAssignment and comparison are mixedName the next score first
user?.profile?.name everywhereMay hide required missing dataGuard optional paths, fail clearly for required paths

Here is the last numeric example fixed:

let score = 9;
const max = 10;

const nextScore = score + 1;
const wouldExceedMax = nextScore > max;

if (!wouldExceedMax) {
  score = nextScore;
}

console.log(score); // 10

The code is longer than a single expression. It is also much harder to misunderstand, which is the point.

Key Takeaways

  • Pick operators by intent: calculate, compare, combine, choose, fall back, update, inspect, or access.
  • + is both numeric addition and string concatenation, so convert deliberately when data arrives as text.
  • || and ?? are not interchangeable. ?? preserves valid falsy values.
  • Ternaries are for choosing values, not hiding side effects.
  • When an expression mixes jobs, split it into named steps before the clever version wins an argument it did not deserve.
Share this article:
javascriptfundamentalsoperatorslogic