Basic operators, maths

You already know most of these from grade-school maths: add with +, multiply with *, subtract with -. JavaScript uses the same symbols, so the basics feel familiar right away.

What follows starts on that familiar ground and then walks into the parts that arithmetic class never taught you: what + does to two strings, how a lone + can convert text into a number, and why the order operators run in matters more than you’d think.

Terms: “unary”, “binary”, “operand”

A little shared vocabulary makes the rest of this page easier to talk about.

An operand is the thing an operator acts on. In 5 * 2, the * has two operands: 5 on the left and 2 on the right. You’ll occasionally hear these called “arguments” instead — same idea.

An operator is unary when it works on a single operand. The unary minus flips a number’s sign:

let x = 1;

x = -x;
alert( x ); // -1, unary negation was applied

An operator is binary when it works on two operands. That same minus sign also has a binary form that subtracts:

let x = 1, y = 3;
alert( y - x ); // 2, binary minus subtracts values

Notice that - played two different roles above. They’re genuinely two separate operators that happen to share a symbol: a unary one that reverses sign, and a binary one that subtracts. JavaScript tells them apart by counting how many operands sit around the symbol.

unary — 1 operand
-x
flips the sign
binary — 2 operands
y-x
subtracts right from left
Same minus sign, two different operators, distinguished by operand count.

Maths

These arithmetic operations are built in:

  • Addition +
  • Subtraction -
  • Multiplication *
  • Division /
  • Remainder %
  • Exponentiation **

The first four behave exactly as you’d expect. The last two deserve a closer look.

Remainder %

Despite looking like a percent sign, % has nothing to do with percentages. a % b gives the remainder left over after dividing a by b using whole-number division.

alert( 7 % 3 ); // 1, the remainder of 7 divided by 3
alert( 9 % 4 ); // 1, the remainder of 9 divided by 4
alert( 6 % 3 ); // 0, the remainder of 6 divided by 3

Think of it as “how much is left after you take out as many full copies of b as you can.” Seven holds two copies of three (that’s six), leaving 1.

3+3+1= 7
two full groups of 3, and a remainder of 1
7 % 3 — pull out whole 3s, and 1 is left standing.

Keep clicking below and watch i climb without limit while i % 7 stays trapped inside 0–6, cycling through the week over and over:

interactivei % 7 wraps a growing counter into a week

Exponentiation **

The a ** b operator raises a to the power of b — what you’d write as ab on paper.

alert( 2 ** 2 ); // 2² = 4  (2 * 2)
alert( 2 ** 3 ); // 2³ = 8  (2 * 2 * 2)
alert( 2 ** 4 ); // 2⁴ = 16 (2 * 2 * 2 * 2)

The exponent doesn’t have to be a whole number. A fractional power is a root: raising to 1/2 is a square root, 1/3 is a cube root, and so on.

alert( 4 ** (1/2) ); // 2, because 1/2 power means the square root
alert( 8 ** (1/3) ); // 2, because 1/3 power means the cube root

String concatenation with binary +

Here’s the first spot where JavaScript diverges from the maths classroom.

Normally + adds numbers. But when either operand is a string, + switches jobs and concatenates — it glues the two together into one longer string:

let word = "sun" + "flower";
alert(word); // sunflower

The rule: if just one side is a string, the other side gets converted to a string first, then they’re joined.

alert( '7' + 4 ); // "74", the number 4 becomes "4"
alert( 4 + '7' ); // "47", same deal, order doesn't matter

Side matters for the order of characters, but not for the outcome being a string. Either way you get text.

both numbers
2 + 24
arithmetic add
a string appears
‘7’ + 4“74”
glue as text
Binary + inspects its operands: any string present means concatenate, otherwise add.

Chains of + are evaluated left to right, one step at a time, and each step decides its behavior on the fly:

alert(3 + 4 + '5' ); // "75" and not "345"

The first + sees two numbers and adds them: 3 + 4 is 7. The second + now faces 7 + '5' — a string is involved, so it concatenates into "75".

Flip the string to the front and the whole chain tips into string mode early:

alert('5' + 4 + 3); // "543" and not "57"

'5' + 4 concatenates to "54", and then "54" + 3 concatenates again to "543". Once a string enters the running total, every following + stays in concatenation mode.

3 + 4 + ‘5’
3 + 4 → 7
7 + ‘5’ → “75”
‘5’ + 4 + 3
‘5’ + 4 → “54”
“54” + 3 → “543”
Same operators, different starting operand — the left-to-right running result changes everything.

Binary + is the only operator with this string talent. Every other arithmetic operator ignores strings-as-text and forces its operands to numbers:

alert( 9 - '3' ); // 6, '3' is converted to the number 3
alert( '8' / '2' ); // 4, both strings become numbers

Numeric conversion, unary +

The + symbol has a unary form too — a single + in front of one value.

On a number, unary + does nothing at all. On anything else, it converts that value to a number:

// No effect on numbers
let x = 5;
alert( +x ); // 5

let y = -8;
alert( +y ); // -8

// Converts non-numbers
alert( +true ); // 1
alert( +"" );   // 0

It’s a shorthand for Number(...) — same conversion, fewer keystrokes.

That conversion comes up constantly, and form fields are the usual reason. Their values arrive as strings, so plain + would concatenate them:

let adults = "6";
let kids = "2";

alert( adults + kids ); // "62", binary plus concatenates the strings

Convert each with a unary + first, and the binary + then adds actual numbers:

let adults = "6";
let kids = "2";

alert( +adults + +kids ); // 8

// the longer, equivalent form:
// alert( Number(adults) + Number(kids) ); // 8

A wall of pluses looks odd, but the logic is orderly: the two unary pluses convert "6" and "2" to numbers first, then the binary plus sums them.

+“6”6and+“2”2
then6+28
+adults + +kids — unary conversions happen first, then the binary add.

This is the exact bug the warning above described, so try it live. Type two values as if they arrived from form fields (they’re strings), and compare plain a + b against +a + +b:

interactiveString glue vs. numeric add

Why do the unary pluses win the race and run before the binary one? Because they carry higher precedence — the topic of the next section.

Operator precedence

When an expression holds more than one operator, precedence decides which runs first. It’s the language’s built-in priority order.

You already rely on this from maths: in 1 + 2 * 2, multiplication happens before addition, giving 5, not 6. Multiplication has higher precedence than addition.

Parentheses beat any precedence rule. Wrap the part you want first and it goes first: (1 + 2) * 2 is 6.

Every JavaScript operator carries a precedence number. Higher numbers run earlier. When two operators tie, they run left to right.

Here’s a slice of the full precedence table. No need to memorize it — the point is that each unary operator outranks its binary twin:

Precedence Name Sign
14 unary plus +
14 unary negation -
13 exponentiation **
12 multiplication *
12 division /
11 addition +
11 subtraction -
2 assignment =

Unary plus sits at 14, well above binary addition’s 11. That gap is precisely why, in +adults + +kids, the conversions happen before the sum.

14unary +  runs first
11binary +  runs next
2=  stores the result last
Higher precedence runs first — unary + (14) converts before binary + (11) adds.

Assignment

= is an operator too, and the table above lists it near the bottom with precedence 2.

That low ranking is why x = 2 * 2 + 1 works the way you’d hope: the arithmetic all resolves first, and only then does = stash the finished value in x.

let x = 2 * 2 + 1;

alert( x ); // 5

Assignment = returns a value

Since = is an operator and not some special language keyword, it follows the operator rules — including that every operator produces a value.

Writing x = value stores value into x and then hands that value back as the result of the expression.

Watch it used mid-expression:

let a = 2;
let b = 3;

let c = 8 - (a = b + 4);

alert( a ); // 7
alert( c ); // 1

(a = b + 4) computes 7, stores it in a, and evaluates to 7. The outer expression then becomes 8 - 7, so c is 1.

It’s clever, and you’ll spot it inside libraries now and then, which is why it’s worth recognizing. Writing your own code this way, though, only makes it harder to read. Don’t reach for it.

Chaining assignments

Because = returns its value, assignments can be chained:

let a, b, c;

a = b = c = 3 + 5;

alert( a ); // 8
alert( b ); // 8
alert( c ); // 8

The chain runs right to left. 3 + 5 is evaluated once, the 8 flows into c, that assignment returns 8, which flows into b, then into a. All three end up holding the same value.

abc8(3 + 5)
evaluation direction: right → left
a = b = c = 3 + 5 evaluates right to left; the value ripples leftward.

For readability, prefer spelling it out across a few lines. It scans faster when you’re skimming:

c = 3 + 5;
b = c;
a = c;

Modify-in-place

A common pattern is to apply an operator to a variable and store the result back into that same variable:

let n = 3;
n = n + 4;
n = n * 5;

The compound assignment operators += and *= shorten this:

let n = 3;
n += 4; // now n = 7  (same as n = n + 4)
n *= 5; // now n = 35 (same as n = n * 5)

alert( n ); // 35

There’s a short “modify-and-assign” form for every arithmetic and bitwise operator: -=, /=, %=, **=, and so on.

These share assignment’s low precedence, so the right-hand side is fully computed before the operator applies:

let n = 3;

n *= 2 + 4; // the right side runs first, so this is n *= 6

alert( n ); // 18

n *= 2 + 4 is n *= 6, giving 18 — not n * 2 + 4 which would be 10.

Increment/decrement

Bumping a number up or down by one is so common it earns dedicated operators.

Increment ++ adds one to a variable:

let counter = 2;
counter++;        // same as counter = counter + 1, just shorter
alert( counter ); // 3

Decrement -- subtracts one:

let counter = 2;
counter--;        // same as counter = counter - 1, just shorter
alert( counter ); // 1

You can write these operators on either side of the variable:

  • After the variable is the postfix form: counter++.
  • Before it is the prefix form: ++counter.

Both bump counter by one. The difference only surfaces when you use the value the operator returns — and here’s the rule that catches everyone:

  • Prefix (++counter) changes the variable, then returns the new value.
  • Postfix (counter++) returns the old value, then changes the variable.
++counter (prefix)
1. counter: 1 → 2
2. returns 2 (new)
counter++ (postfix)
1. returns 1 (old)
2. counter: 1 → 2
Prefix returns the value after the change; postfix returns it before.

Prefix in action — the new value comes back:

let counter = 1;
let a = ++counter; // (*)

alert(a); // 2

On line (*), ++counter raises counter to 2 and returns that 2, so a is 2.

Now postfix, on otherwise identical code:

let counter = 1;
let a = counter++; // (*)

alert(a); // 1

Here counter++ still raises counter to 2, but it returns the value from before the bump, 1. So a is 1, even though counter is now 2.

Run both forms yourself. Each button captures what the operator hands back into a, then shows where counter landed — notice they bump the variable identically but return different values:

interactiveWhat ++counter returns vs. counter++

Putting the forms side by side:

  • When you ignore the return value, the two forms are interchangeable:

    let counter = 0;
    counter++;
    ++counter;
    alert( counter ); // 2, both lines did the same thing
  • When you want the value after incrementing, use prefix:

    let counter = 0;
    alert( ++counter ); // 1
  • When you want the value from before incrementing, use postfix:

    let counter = 0;
    alert( counter++ ); // 0

Bitwise operators

Bitwise operators view their operands as 32-bit integers and operate directly on the individual binary digits (bits).

They aren’t unique to JavaScript — nearly every language has them.

The full set:

  • AND ( & )
  • OR ( | )
  • XOR ( ^ )
  • NOT ( ~ )
  • LEFT SHIFT ( << )
  • RIGHT SHIFT ( >> )
  • ZERO-FILL RIGHT SHIFT ( >>> )

You’ll rarely need these in everyday web work. They matter in niches like cryptography, low-level data packing, graphics, and permission flags. When one of those tasks lands on your desk, MDN’s Bitwise Operators guide is the reference to reach for.

Comma

The comma operator , is among the rarest and strangest. You mostly need to know it so that unfamiliar code doesn’t confuse you.

It lets you evaluate several expressions in a row, separated by commas. Every expression runs, but only the last one’s result is kept:

let a = (5 + 1, 2 + 6);

alert( a ); // 8, the result of 2 + 6

5 + 1 is evaluated and its 6 is discarded. Then 2 + 6 runs and its 8 becomes the value of the whole thing.

So why would anyone want an operator that keeps only the last value? Occasionally it’s used to fit several actions into one slot where the language expects a single expression.

A for loop header is the usual home for it:

// three setup actions in one line
for (a = 1, b = 3, c = a * b; a < 10; a++) {
 // ...
}

You’ll see this in plenty of frameworks and minified code, which is the reason it’s worth recognizing. It rarely makes code clearer, though, so think twice before writing it yourself.