JavaScript's arithmetic operators behave the way you would expect until two of them meet a string. The seven operators — + - * / % ** and the compound assignment versions — are simple in isolation, and confusing when you mix them with concatenation, precedence and the difference between ++x and x++.
Which arithmetic operators does JavaScript have?
Seven, and they cover everything from school arithmetic to exponentiation.
console.log(7 + 3); // 10 addition
console.log(7 - 3); // 4 subtraction
console.log(7 * 3); // 21 multiplication
console.log(7 / 3); // 2.3333333333333335 division
console.log(7 % 3); // 1 remainder
console.log(7 ** 3); // 343 exponentiation
console.log(-7); // -7 unary minus
Two of these behave in ways worth memorising.
Division never truncates. 7 / 2 is 3.5, not 3. JavaScript has no integer division, so if you need one you have to ask for it explicitly with Math.trunc (rounds towards zero) or Math.floor (rounds down). The difference matters with negative numbers: Math.floor(-3.5) is -4 while Math.trunc(-3.5) is -3.
The % operator is a remainder, not a modulo. The sign of the result follows the dividend, the number on the left.
console.log(10 % 4); // 2
console.log(10 % 5); // 0
console.log(-7 % 3); // -1 ← sign follows the dividend
console.log(7 % -3); // 1
console.log(7 / 2); // 3.5
console.log(Math.trunc(7 / 2)); // 3
If you need a true modulo that always returns a non-negative result — useful for wrapping an index around an array — add the divisor when the result comes out negative: ((value % n) + n) % n.
What is the difference between ++x and x++?
Both add one. The difference is when the addition happens relative to the value being used. The prefix form increments first and yields the new value. The postfix form yields the old value and increments afterwards.
let a = 5;
console.log(a++); // 5 — uses the old value, then increments
console.log(a); // 6
let b = 5;
console.log(++b); // 6 — increments first, then uses the new value
console.log(b); // 6
let c = 5;
console.log(c--); // 5
console.log(c); // 4
This matters in exactly one situation: when the value is used in the same expression as the increment. console.log(a++) prints 5 and leaves a at 6. console.log(++a) prints 6 and leaves a at 6 too.
As a general style rule, prefer the compound form a += 1 in your own code. It is unambiguous, it reads the same whichever side you start from, and it works for values that are not plain numbers. Using ++ as a statement on its own is fine; using it inside a larger expression is where the confusion starts.
What do the compound assignment operators save you?
They combine an operation and an assignment into one operator. x += 5 means x = x + 5.
let stock = 12;
stock += 5; // 17
stock -= 3; // 14
stock *= 2; // 28
stock /= 4; // 7
stock %= 4; // 3
stock **= 2; // 9
console.log(stock);
Beyond saving a few characters, the compound form has a real advantage: it reads the variable once. If the right-hand side also touches the variable, x = x + x++ and x += x++ can behave differently because the order of evaluation is subtle. The compound form sidesteps that question entirely.
It also changes the type behaviour. total += 1 where total holds a string appends the number as text, because + with a string on the left concatenates. That is the next section, and it is the single most common source of a JavaScript bug that looks like a typo.
Why is the + operator special?
Because + is overloaded: it adds numbers and it joins strings, and which one it does depends on the types involved. If either operand is a string, the operation becomes concatenation.
const first = "10";
const second = 5;
console.log(first + second); // "105" ← string wins
console.log(first - second); // 5 ← the other way round
console.log("Total: " + 20 + 5); // "Total: 205"
console.log("Total: " + (20 + 5)); // "Total: 25"
console.log(20 + 5 + " rupees"); // "25 rupees"
Look at lines three and four. The first computes "Total: " + 20 first, which produces a string, and then adds 5 to that string. The second brackets the arithmetic so it happens before the string is involved. Adding parentheses is the fix.
Note the asymmetry in lines two and five. - has no string mode, so "10" - 5 converts the string to a number and subtracts. + looks at the left operand first: in 20 + 5 + " rupees" the first addition is number plus number, so it is 25, and only then does the string join in.
As a practical rule, do not use + to add things when any of them might be a string. Convert explicitly first with Number(), or use a template literal to build text and keep arithmetic separate.
What order do the operators run in?
JavaScript's precedence table is long, but the operators you will actually meet fit into a short list. Multiplication and division come before addition and subtraction, and ** comes above everything else.
console.log(2 + 3 * 4); // 14
console.log((2 + 3) * 4); // 20
console.log(2 ** 3 ** 2); // 512 ← right-associative: 2 ** (3 ** 2)
console.log(10 - 4 - 3); // 3 ← left-associative: (10 - 4) - 3
console.log(true + 1); // 2 ← a boolean coerces to a number
console.log("5" * "2"); // 10 ← * always coerces to numbers
Two details here catch people out. Exponentiation is the only right-associative arithmetic operator, so 2 ** 3 ** 2 is 512, not 64. And addition is left-associative, so a chain of subtractions groups to the left.
Coercion is the other surprise. true + 1 is 2 because a boolean converts to 1 before the addition. "5" * "2" is 10 because multiplication has no string mode, so both operands become numbers. When an operator gives you a result you did not expect, check the operand types before you check your logic.
The arithmetic rules worth remembering
- There are seven operators:
+ - * / % **plus unary minus and plus. Division always produces a float. %is a remainder, not a modulo. The sign follows the dividend, so-7 % 3is-1.++xincrements then yields;x++yields then increments. Preferx += 1in expressions.- Compound assignment (
+=,-=,*=) reads the variable once and reads better thanx = x + .... +concatenates when either side is a string. Bracket your arithmetic.**is right-associative; everything else groups left to right.
Try it yourself: take a shopping total and a discount and work out the final price using (a) total - discount, (b) total += -discount and (c) a template literal that builds the sentence "You saved X". Then change the discount to the string "50" and run all three again. Only the first two keep doing arithmetic; the third happily produces text, and that difference is the bug you just avoided.