Arithmetic operators

An operator combines values into a new value. The arithmetic operators work on numbers:

Operator Meaning Example Result
+ addition 7 + 2 9
- subtraction 7 - 2 5
* multiplication 7 * 2 14
/ division 7 / 2 3.5
% remainder 7 % 2 1
** exponentiation (ES2016) 2 ** 10 1024
console.log(7 / 2, 6 / 2);
console.log(7 % 2, -7 % 2);
console.log(Math.floor(7 / 2), Math.trunc(-7 / 2), Math.floor(-7 / 2));
console.log(1 / 0, -1 / 0, 0 / 0);
console.log(0.1 + 0.2);
3.5 3
1 -1
3 -3 -4
Infinity -Infinity NaN
0.30000000000000004

What this shows:

  • There is one number type, so / simply divides: 7 / 2 is 3.5 and 6 / 2 is 3. For whole-number division, round the result yourself with Math.floor() (always down) or Math.trunc() (toward zero); they differ for negative numbers.
  • % is the remainder, and its sign follows the left operand: -7 % 2 is -1.
  • Dividing by zero is not an error: it gives Infinity, -Infinity, or NaN ("not a number") for 0 / 0.
  • Decimals are binary floating-point numbers, so 0.1 + 0.2 is not exactly 0.3 (see why 0.1 + 0.2 is not 0.3 and Numbers, Math and Dates).

Increment and decrement

++ adds 1 to a variable and -- subtracts 1. Written after the variable (postfix), the expression gives the old value; written before it (prefix), it gives the new value. The variable changes either way:

let tickets = 5;
console.log(tickets++);   // gives 5, then tickets becomes 6
console.log(tickets);
console.log(++tickets);   // tickets becomes 7, then gives 7
console.log(tickets--, tickets);
5
6
7
7 6

On a line of its own, count++ and ++count do the same thing. The difference only matters when the expression is used inside a larger one — which is a good reason not to do that.

Assignment operators

= assigns a value. The compound operators combine an operation with assignment: total += kwh means total = total + kwh. They exist for every arithmetic operator (-=, *=, /=, %=, **=), and since ES2021 also for the logical operators (&&=, ||=, ??=; see below). An assignment is itself an expression whose value is the assigned value, which is why a = b = 0 works.

Type coercion

Type coercion is the automatic conversion of a value to another type when an operator needs it. Where Python raises a TypeError for "5" + 3, JavaScript converts one side and carries on. That is what makes JavaScript weakly typed. The rules for the arithmetic operators are short:

  • + with a string on either side joins text. The other side is converted to a string.
  • All other arithmetic operators convert to numbers. -, *, /, % and ** turn both sides into numbers, and a string that is not a valid number becomes NaN.
  • Operators work left to right. So 1 + 2 + " kWh" first adds the numbers.
console.log("5" + 3, 3 + "5");
console.log("5" - 3, "5" * "2", "10" / "4");
console.log(1 + 2 + " kWh", "kWh " + 1 + 2);
console.log("12 kWh" * 2);
console.log(true + 1, null + 1, undefined + 1);
console.log([1, 2] + [3], [] + []);
53 35
2 10 2.5
3 kWh kWh 12
NaN
2 1 NaN
1,23

The last lines show the stranger conversions: true becomes 1, null becomes 0, undefined becomes NaN, and arrays are first turned into strings ([1, 2] becomes "1,2", an empty array becomes an empty string). You do not need to memorize all of this. You need to recognize it when a bug shows "312.510" where you expected 322.5.

Comparison operators

The relational operators <, >, <= and >= return a boolean. When both sides are strings, they compare text character by character by character code (UTF-16 code units), not alphabetically as a person would. Otherwise both sides are converted to numbers:

console.log(118.2 < 121.4);
console.log("10" < "9");      // two strings: compared as text
console.log("10" < 9);        // one number: compared as numbers
console.log("Zwolle" < "apeldoorn", "Zwolle" < "Apeldoorn");
console.log("12 kWh" < 20, "12 kWh" >= 20);
true
true
false
true false
false false

"10" < "9" is true because the character "1" comes before "9". Uppercase letters all come before lowercase ones, so "Zwolle" sorts before "apeldoorn". For human-friendly ordering, use localeCompare() (see Strings and Template Literals). The last line shows that any comparison with NaN is false, so a value can be neither smaller nor greater than or equal to another.

Strict equality: === and !==

Strict equality, ===, is true only when both values have the same type and the same value. It never converts anything. Its opposite is !==. This is the comparison to use by default:

console.log(5 === 5, 5 === "5");
console.log("North" === "North", "North" === "north");
console.log(null === undefined);
console.log(5 !== "5");
true false
true false
false
true

Loose equality: == and !=

Loose equality, ==, first converts the two values to a common type when their types differ, and then compares them. Its opposite is !=. The conversion rules are long and produce results that are hard to predict:

console.log(5 == "5");
console.log("" == 0, "0" == 0, "" == "0");
console.log(false == "0", false == "");
console.log(null == undefined, null == 0);
console.log([] == false, [] == "");
true
true true false
true true
true false
true true

Look at the second line: "" equals 0, and "0" equals 0, but "" does not equal "0". Equality that is not transitive is a reliable source of bugs. Use ===, and convert values explicitly when their types may differ.

One use of == is common and deliberate: value == null is true for both null and undefined and for nothing else, so it checks "is there no value?" in one comparison. Many style guides allow exactly this exception.

NaN and comparing objects

Two cases trip up both == and ===. NaN is not equal to anything, not even to itself; test for it with Number.isNaN(). And objects (including arrays) are equal only when they are the same object, not when they have the same contents (see values and references):

const reading = Number("n/a");
console.log(reading === NaN, Number.isNaN(reading));

const a = [118.2, 119.0];
const b = [118.2, 119.0];
console.log(a === b, a === a);
console.log(JSON.stringify(a) === JSON.stringify(b));
false true
false true
true

Comparing the JSON text of two simple arrays or objects is a quick way to compare contents, as the last line does, but it depends on the order of keys. Testing libraries offer a proper "deep equal".

Logical operators

The logical operators are && (and), || (or) and ! (not). With booleans they behave as in Boolean logic, and && and || use short-circuit evaluation: they stop as soon as the result is known. Two details make them more powerful than plain true/false logic.

Truthy and falsy values

Every value counts as either true or false when it is used as a condition (see truthy and falsy). In JavaScript exactly these values are falsy:

  • false
  • 0, -0 and 0n (BigInt zero)
  • "" (the empty string)
  • null and undefined
  • NaN

Everything else is truthy, including "0", "false", " ", empty arrays [] and empty objects {}. !!value (not-not) or Boolean(value) shows how a value counts:

const values = [0, "", null, undefined, NaN, "0", "false", [], {}];
for (const value of values) {
  console.log(value, "is", Boolean(value) ? "truthy" : "falsy");
}
0 is falsy
 is falsy
null is falsy
undefined is falsy
NaN is falsy
0 is truthy
false is truthy
[] is truthy
{} is truthy

&& and || return an operand

a || b returns a if it is truthy, and otherwise b. a && b returns a if it is falsy, and otherwise b. The result is one of the two values, not a converted boolean. Only ! always returns a boolean:

console.log("North" || "Unknown");
console.log("" || "Unknown");
console.log("INC-20417" && "has an id");
console.log(0 && "never reached");
console.log(!"North", !0);
North
Unknown
has an id
0
false true

Older code uses || to supply a default value: const region = input || "Unknown". That works for strings, but it also replaces values that are valid yet falsy, such as 0 or an empty string. The next operator fixes that.

The nullish coalescing operator ??

The nullish coalescing operator, ?? (ES2020), returns its right-hand side only when the left-hand side is null or undefined ("nullish"). Every other value, including 0, "" and false, is kept:

const settings = { retries: 0, label: "", timeoutMs: undefined };

console.log(settings.retries || 3, settings.retries ?? 3);
console.log(settings.label || "(none)", settings.label ?? "(none)");
console.log(settings.timeoutMs || 5000, settings.timeoutMs ?? 5000);

settings.timeoutMs ??= 5000;    // assign only if null or undefined
settings.retries ??= 3;         // stays 0
console.log(settings);
3 0
(none)
5000 5000
{ retries: 0, label: '', timeoutMs: 5000 }

A configured retry count of 0 means "do not retry"; || silently turns it into 3, while ?? respects it. Use ?? for defaults, and || only when you really want to replace every falsy value.

Optional chaining ?.

Reading a property of undefined or null throws a TypeError, and data from an API often has gaps. Optional chaining, ?. (ES2020), stops and returns undefined as soon as the value on its left is null or undefined, instead of failing:

const tickets = [
  { id: "INC-20417", assignee: { name: "Amara Okafor", team: "Service desk" } },
  { id: "INC-20418", assignee: null },
];

for (const ticket of tickets) {
  const name = ticket.assignee?.name ?? "unassigned";
  console.log(ticket.id, name);
}

console.log(tickets[1].assignee?.team.toUpperCase());
console.log(tickets[5]?.id);
console.log(tickets[0].notify?.("Ticket updated"));
INC-20417 Amara Okafor
INC-20418 unassigned
undefined
undefined
undefined

The three forms:

  • obj?.prop — read a property
  • obj?.[expression] — read a property or array element by key or index
  • func?.(args) — call a function only if it exists

Once ?. stops, the rest of the chain is skipped too, which is why assignee?.team.toUpperCase() does not fail when assignee is null. Combined with ?? it gives a default in one line, as in the loop above.

Do not sprinkle ?. everywhere. If a value should always be present, a missing value is a bug, and an error that points at it is more useful than an undefined that surfaces three functions later. Use ?. where a value is genuinely optional.

Other operators

Operator What it does
c ? a : b The conditional (ternary) operator: a if c is truthy, otherwise b (see conditional expression)
typeof x The type of a value as a string (see typeof)
x instanceof C true if the object x was created by the class or constructor C (or one that inherits from it)
"key" in obj true if the object has a property with that name
delete obj.key Removes a property from an object; returns true when the property is gone afterward
const kwh = 412;
console.log(kwh > 400 ? "high usage" : "normal usage");

const readings = [118.2, 119.0];
const issuedOn = new Date(Date.UTC(2026, 9, 1));
console.log(readings instanceof Array, issuedOn instanceof Date);
console.log(readings instanceof Object, "text" instanceof String);

const ticket = { id: "INC-20417", status: "open", draftNote: "check meter" };
console.log("draftNote" in ticket);
console.log(delete ticket.draftNote);
console.log("draftNote" in ticket, ticket);
high usage
true true
true false
true
true
false { id: 'INC-20417', status: 'open' }

instanceof works for objects only: "text" is a primitive string, not a String object, so the result is false. Use typeof for primitives and instanceof for objects. delete removes properties, not variables; to empty a variable, assign null or undefined. Classes and constructors are explained in Classes, Prototypes and this.

Precedence and associativity

When an expression has several operators, operator precedence decides which one is applied first, and associativity decides the order among operators of equal precedence. From highest to lowest, for the operators on this page:

Operators Associativity
( … ) grouping —
. ?. [ ] ( ) member access and calls left to right
x++ x-- (postfix) —
! -x +x ++x --x typeof delete (prefix) —
** right to left
* / % left to right
+ - left to right
< <= > >= in instanceof left to right
== != === !== left to right
&& left to right
|| ?? left to right
? : = += -= … ??= right to left
console.log(2 + 3 * 4, (2 + 3) * 4);
console.log(2 ** 3 ** 2);        // right to left: 2 ** 9
console.log(20 - 5 - 3);         // left to right: (20 - 5) - 3
console.log(!0 === true);        // (!0) === true

let a, b;
a = b = 7;                       // right to left: b = 7, then a = 7
console.log(a, b);
14 20
512
12
true
7 7

Two combinations are so ambiguous that JavaScript refuses them with a SyntaxError: a unary minus directly before ** (write (-2) ** 2 or -(2 ** 2)), and mixing ?? with && or || without parentheses:

console.log((-2) ** 2, -(2 ** 2));
console.log(-2 ** 2);
SyntaxError: Unary operator used immediately before exponentiation expression. Parenthesis must be used to disambiguate operator precedence

Notice that not even the first line ran: a syntax error is found when the engine parses the file, before any of it executes.

Browser dialogs: alert, confirm and prompt

Browsers offer three built-in dialog boxes for simple interaction. They are host APIs of the browser, not part of the language, so they do not exist in Node.js. This code runs in a browser:

alert("Planned maintenance tonight from 22:00 to 23:00.");

const ok = confirm("Close ticket INC-20417?");   // true (OK) or false (Cancel)
console.log("Close confirmed:", ok);

const answer = prompt("Meter reading in kWh:", "0");  // a string, or null on Cancel
if (answer === null) {
  console.log("Cancelled");
} else {
  const kwh = Number(answer);
  console.log(Number.isNaN(kwh) ? "Not a number" : `Reading: ${kwh} kWh`);
}

If the user clicks OK in the confirm dialog and then types 312.5, the console shows:

Close confirmed: true
Reading: 312.5 kWh
  • alert(message) shows a message with an OK button and returns undefined.
  • confirm(message) shows OK and Cancel and returns true or false.
  • prompt(message, default) shows a text field and returns what the user typed as a string, or null when they press Cancel. Convert the string before calculating with it.

All three are modal and blocking: the page cannot be used and your script pauses until the user closes the dialog. Their look cannot be styled, and browsers may suppress them, for example in some embedded frames. Real applications therefore use their own dialogs and form fields built with HTML (see The DOM and Events). The three functions remain useful for quick experiments and appear on the JSE exam.