All About Javascript Operators: Clean Code Tips, Gotchas & Best Practices

Table of contents
- ๐ข What Are Operators & Expressions?
- ๐งฎ Expressions vs Statements: What's the Difference?
- ๐งโ๐ง Categories of Javascript Operators
- โ Arithmetic Operators
- ๐งฑ Assignment Operators
- โ Comparison Operators
- โฑ๏ธ Logical Operators
- ๐ String Concatenation & Template Literals
- ๐ Unary: UnaryPlus, Unary Minus, Increment& Decrement Operators
- ๐ง Ternary (Conditional) Operator - Elvis Operator nickname ๐ค
- ๐ Type Operators: typeof, delete, in, instanceof
- ๐งฉ Bitwise Operators
- โจ Modern Javascript Operators: ..., ??, ?.
- ๐ Operator Precedence & Associativity
- ๐ง Common Mistakes to Avoid

Operators are like the buttons on our coding toolbox โ they help us calculate, compare, assign values, and stitch data together. Without them, our code would accomplish very little.
Whether we're crunching numbers, building dynamic messages, or checking conditions, operators make it happen.
Let's break them down and see how each one powers our everyday programming ๐ช.
๐ข What Are Operators & Expressions?
Operators perform actions (e.g.,
+
,===
,&&
).Expressions combine values and operators, like mini math problems inside code.
Analogy: Think of operators as math symbols (+
, โ
) and expressions as the full question โ "What's 2 + 3 * 4
?" โ in maths class.
๐งฎ Expressions vs Statements: What's the Difference?
Expressions produce a value:
2 + 2
,user.name
,isLoggedIn && "Yes"
Statements perform actions:
if (x > 5) { ... }
,let total = 50
Expressions can often be used inside statements โ such as placing a mini-calculation within an if statement.
๐งโ๐ง Categories of Javascript Operators
Before we dive in, let's quickly look at two common ways to categorise Javascript operators:
โ By Purpose: What the Operator Does
Category | Examples |
Arithmetic | + , - , * , / , % |
Assignment | = , += , -= , *= , /= , %= |
Comparison | == , === , != , !== , < , > |
Logical | && , ` |
String Concatenation | + , template literals (`Hello ${name}` ) |
Unary Plus & Minus, Increment/Decrement | ++, -- , + , - , ! |
Ternary Conditional | ? : |
Type Operators | typeof , delete , in , instanceof |
Bitwise | & , ` |
Modern Helpers | ... , ?? , ?. , (optional chaining, spread, nullish coalescing) |
We'll explore each of these below, with clear examples.
๐ By Number of Operands: Unary, Binary, Ternary
Javascript also classifies operators by how many values (operands) they work with:
- Unary operators: Work on one value.
typeof "Hello"; // "string"
- Binary operators: Work between two values.
5 + 10; // 15
- Ternary operators: Work with three parts. Javascript has only one ternary operator:
let message = isLoggedIn ? "Welcome" : "Please log in";
Don't worry if this feels abstract โ we'll break these down with real examples in the next sections.
Ready? Let's dive in ๐คฟ.
โ Arithmetic Operators
Arithmetic operators help us perform the usual math operations:
+
โ Adds two numbers.-
โ Subtracts one number from another.*
โ Multiplies two numbers./
โ Divides one number by another.%
โ Modulo operator - It gives the remainder of a division.**
โ Exponentiation - It raises a number to a power.
Example usage:
let price = 10;
let tax = price * 0.2; // 2
let total = price + tax; // 12
let next = price % 3; // 1 -> 10 divided by 3, gives 1 as reminder
let raised = 2 ** 3; // 8 -> 2 to the power of 3
โ ๏ธ Gotchas to Watch For
If you add a string with a number using
+
, Javascript will treat it as string concatenation:Dividing by
0
doesn't throw an error. It returnsInfinity
๐ค:
console.log(10 / 0); // Infinity
โ๏ธ Arithmetic operators are straightforward, but always double-check when working with mixed data types or division.
๐งฑ Assignment Operators
Assignment operators help us put values into variables and update them easily.
=
assigns a value to a variable. Like sticking a label on a box ๐ท๏ธ.+=
,-=
,*=
,/=
,%=
are shortcuts that update the variable and reassign the result back. Instead of writing it twice, Javascript does it for us.
Example:
let score = 10;
score += 5; // score is now 15
This is the same as writing:
score = score + 5;
We'll see this a lot when tracking scores, totals, counters, or anything that changes over time.
No big gotchas here โ just a helpful way to write cleaner, shorter code.
โ Comparison Operators
Comparison operators help us ask Javascript important yes/no questions:
"Are these values equal?"
"Is this number bigger?"
"Should this condition pass?"
==
and!=
are loose comparisons โ they allow Javascript to convert (or "coerce") the types if they don't match.===
and!==
are strict comparisons โ they check both the value and the type, no guessing allowed.We also have the usual suspects:
>
,<
,>=
, and<=
for comparing numbers.
Example:
5 === "5"; // false โ strict, types differ
5 == "5"; // true โ loose, Javascript converts "5" to 5
โ ๏ธ Here's the deal: Always prefer ===
and !==
unless we have a very specific reason to allow type coercion. It'll save us from some of Javascript's weirder bugs.
We'll dive deeper into the difference between ==
and ===
in a later article โ and trust us, that rabbit hole is worth exploring ๐ณ๏ธ.
โฑ๏ธ Logical Operators
Logical operators help us combine multiple conditions and make smarter decisions in our code.
&&
โ AND: both sides must be true.||
โ OR: only one side needs to be true.!
โ NOT: flips the truthiness of a value.
Example in action:
if (isLoggedIn && hasPaid) {
// Only allow access if both conditions are true
}
let display = userName || "Guest";
// If userName exists, use it. Otherwise, fall back to "Guest"
let isLoading = true;
if(!isLoading) {
// This means that if isLoading is false, load the content.
}
These operators use something called short-circuit logic, meaning Javascript stops checking as soon as it finds the answer:
With
&&
, if the first condition isfalse
, Javascript skips the second one โ no need to check further.With
||
, if the first condition istrue
, Javascript returns it right away.
โ๏ธ This saves time and keeps our code efficient, especially when the second part of the check involves a function call or a longer expression.
๐ String Concatenation & Template Literals
+
joins stringsBackticks `
${}
` make it clean
As we have already discussed about template literals in the previous article, here is a quick recap.
let name = "SK";
let msg = `Welcome, ${name}!;`
Template literals handle formatting, multiline text, and embedded variables beautifully.
๐ Unary: UnaryPlus, Unary Minus, Increment& Decrement Operators
These tiny operators help us quickly adjust numbers or flip their signs. We use them all the time in loops, counters, and quick calculations.
Here's what we've got:
++
โ Increments a number by 1.--
โ Decrements a number by 1.+value
โ Converts the value into a number (in case it was a string or boolean).-value
โ Flips the sign of a number.
let x = 2;
console.log(++x); // 3 โ pre-increment: add first, return the new value
console.log(x--); // 3 โ post-decrement: return first, then subtract 1
console.log(x); // 2
๐ Pre-Increment vs Post-Increment
The placement of ++
or --
changes how Javascript runs it:
- Pre-increment (
++x
): Increments the variable first, then gives you the updated value.
let a = 5;
console.log(++a); // 6 โ increments first
console.log(a); // 6
- Post-increment (
x++
): Returns the current value first, then increments it behind the scenes.
let b = 5;
console.log(b++); // 5 โ returns first
console.log(b); // 6 โ now itโs incremented
The same logic applies to --
when we're subtracting instead of adding.
โ๏ธ These operators are small but powerful. Just make sure we pick pre- or post-based on when we want the new value vs. the original one.
โ Unary Plus (+
)
Converts whatever we give it into a number.
let str = "42";
console.log(+str); // 42 (as a number)
let bool = false;
console.log(+bool); // 0
โ Unary Minus (-
)
Also converts to a number, then flips the sign.
let num = "10";
console.log(-num); // -10
โ ๏ธ Small Gotcha:
If we try to convert something that can't be turned into a number, we'll get NaN
:
console.log(+"hello"); // NaN
โ๏ธ These unary operators are small but powerful โ perfect for quick conversions or adjustments without writing a whole function.
๐ง Ternary (Conditional) Operator - Elvis Operator nickname ๐ค
The ternary operator gives us a quick and clean way to write simple conditional expressions โ basically a shortcut for small if/else decisions.
It looks like this:
condition ? valueIfTrue : valueIfFalse;
Example:
let isLoggedIn = true;
let msg = isLoggedIn ? "Welcome" : "Please Log In";
console.log(msg); // "Welcome"
โ๏ธ We use it when we need to assign a value or return something based on a condition, all in one line.
โ ๏ธ But don't overuse it. If your conditions get complicated (or nested ternaries start appearing ๐คฏ), it's much better to switch to a clear if/else
.
๐ Quick Note:
We'll explore if, else if, and else in detail in the following article on Control Flow Statements, where we'll show how to make more complex decisions in your code without getting tangled up in ternary spaghetti ๐.
๐ Type Operators: typeof
, delete
, in
, instanceof
Javascript gives us some handy built-in operators to check types, remove properties, and test relationships between objects and constructors. Let's break them down:
typeof
Type: Unary (works on one value).
What it does: Tells us the data type of a value.
Example:
console.log(typeof 42); // "number"
console.log(typeof "hello"); // "string"
console.log(typeof []); // "object"
Yeah, we've already seen plenty of typeof
in our earlier articles ๐ โ it's the go-to for checking what something is.
delete
Type: Unary.
What it does: Removes a property from an object.
Example:
let user = { name: "SK", age: 30 };
delete user.age;
console.log(user); // { name: "SK" }
โ ๏ธ Heads-up:
delete
removes object properties, but can't delete variables.On arrays, it leaves an empty slot (it doesn't reindex).
in
Type: Binary (needs two values).
What it does: Checks if a property exists in an object (or its prototype chain).
Example:
let user = { name: "SK", age: 30 };
console.log("name" in user); // true
console.log("salary" in user); // false
instanceof
Type: Binary.
What it does: Checks if a specific constructor created an object.
Example:
let date = new Date();
console.log(date instanceof Date); // true
console.log(date instanceof Object); // true
โ๏ธ These are essential when we deal with objects, constructors, and prototypes.
The rest of these operators (delete
, in
, instanceof
) โ we'll explore them more deeply in our upcoming articles about Javascript objects.
๐งฉ Bitwise Operators
Bitwise operators perform operations on the binary (bit-level) representation of numbers.
Here's what we have:
&
โ AND|
โ OR^
โ XOR (exclusive OR)~
โ NOT (flips all the bits)<<
โ Shift bits to the left>>
โ Shift bits to the right
Example:
let a = 5; // 0101 in binary
let b = 3; // 0011 in binary
console.log(a & b); // 1 (0001)
console.log(a | b); // 7 (0111)
Let's break this down without going full robot ๐ค:
let a = 5; // which is 0101 in binary
let b = 3; // which is 0011 in binary
โก๏ธ &
โ Bitwise AND
&
compares each bit of a
and b
.
It returns 1
only if both bits are 1
; otherwise, it returns 0
.
0101 (which is 5)
& 0011 (which is 3)
--------
0001 (which is 1)
So, console.log(a & b)
; gives 1
.
โก๏ธ |
โ Bitwise OR
|
compares each bit too, but this time it returns 1
if either bit is 1
.
0101 (which is 5)
| 0011 (which is 3)
--------
0111 (which is binary for 7)
So, console.log(a | b);
gives 7
.
โ๏ธ Where We Use Bitwise Operators
โ๏ธ In low-level operations like:
Graphics manipulation (pixels are often binary).
Encryption algorithms.
Compression tools.
Network protocols.
Hardware-level programming.
โ ๏ธ Gotchas & Why We Rarely Use Them in Web Apps
Bitwise operators force Javascript numbers into 32-bit signed integers, which can produce confusing results if we're not expecting it.
They don't work well for large numbers (like floating-point math or
BigInt
).Most web applications don't work directly with binary data, so we rarely use these unless we're doing something very specific.
If we stick to UI logic, forms, or APIs, we might never touch them. But if we're ever writing a compression library or tinkering with performance tweaks... they're your friends.
โจ Modern Javascript Operators: ...
, ??
, ?.
Modern Javascript introduced some powerful new operators to solve everyday problems in a cleaner, less error-prone way.
If these feel a little tricky right now โ no worries! We'll unleash their full power as we go deeper into working with objects, arrays, and real-world apps. ๐
๐น Spread / Rest Operator (...
)
Introduced in ES6 (2015), this operator wears two hats โ spread and rest โ depending on where we use it.
๐ Spread (...
) โ Unpack Things
Spread helps us unpack elements from arrays or properties from objects into new variables.
It saves us from writing loops or manual copying.
- Without
...
(Manual merging):
let arr1 = [1, 2, 3];
let arr2 = [4, 5];
let combined = arr1.concat(arr2); // Old way
console.log(combined); // [1, 2, 3, 4, 5]
- With
...
(Clean & easy):
let arr1 = [1, 2, 3];
let arr2 = [4, 5];
let combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4, 5]
let numbers = [1, 2, 3];
let newNumbers = [...numbers, 4, 5];
console.log(newNumbers); // [1, 2, 3, 4, 5]
โ๏ธ Also works beautifully with objects:
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let merged = { ...obj1, ...obj2 };
console.log(merged); // { a: 1, b: 2 }
let user = { name: "Luna" };
let details = { age: 28 };
let profile = { ...user, ...details };
console.log(profile); // { name: "Luna", age: 28 }
๐ Rest (...
) โ Collect Things
Rest gathers multiple values into a single array parameter, which is the opposite of spread. Even though they look the same (...
), rest and spread do opposite things depending on where we use them.
function sum(...numbers) {
console.log(numbers); // [1, 2, 3, 4]
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
In the example above, all the arguments passed into the sum()
function are collected into the numbers
array.
๐ก๏ธ What It Solves:
Before ...
, combining or unpacking data required manual loops or extra functions. This made copying objects, merging arrays, or accepting flexible arguments much easier.
โ ๏ธ Gotcha:
When we use the spread operator to merge objects, if they have the same property, the last one in the spread order overwrites the earlier one.
Example:
let user = { name: "Luna", age: 25 };
let updatedInfo = { age: 30, city: "Paris" };
let merged = { ...user, ...updatedInfo };
console.log(merged);
// { name: "Luna", age: 30, city: "Paris" }
See what happened?
user
hadage: 25
.But
updatedInfo
also had age, so it overwrote it with30
.The final object has the latest value (
30
).
โ๏ธ Pro tip: Always put the newest or overriding data last when spreading objects. That's how we ensure the correct values prevail.
๐น Nullish Coalescing Operator (??
)
Introduced in ES11 / ES2020, Nullish coalescing is like a smarter version of ||
(OR).
Imagine you're asking your friend if they have a favourite snack.
If they don't answer (null
or undefined
), you decide to give them cookies ๐ช by default.
That's what ??
does. It checks if something is missing (null
or undefined
) and, if so, provides a default value.
Example:
let favoriteSnack = null;
let snack = favoriteSnack ?? "Cookies";
console.log(snack); // "Cookies"
So:
If
favoriteSnack
is missing (null
orundefined
), we use"Cookies"
as a default.If it's anything else (even
0
,false
, or""
), we keep it.
โ ๏ธ Why not just use ||
?
Because ||
would replace 0
, false
, or empty strings - โโ
, but ??
only replaces null
or undefined
.
Without ??
(Using ||
, but accidentally replacing 0
/false
/""
):
let favoriteSnack = "";
let snack = favoriteSnack || "Cookies";
console.log(snack); // "Cookies" โ โ we didnโt want to replace ""!
With ??
(Only replaces null
or undefined
):
let favoriteSnack = "";
let snack = favoriteSnack ?? "Cookies";
console.log(snack); // "" โ
โ because favoriteSnack is NOT null/undefined
let userName;
console.log(userName ?? "Guest"); // "Guest" โ fallback happens because userName is undefined
๐ก๏ธ What It Solves:
With ||
, Javascript treats falsy values like 0
, ""
, or false
as missing โ which can mess up your logic.
??
gives us more precise control by only replacing nullish values.
โ ๏ธ Gotcha:
If we accidentally use ||
, we might override valid values like 0
or false
. ?? avoids that mistake.
๐น Optional Chaining Operator (?.)
Introduced in ES11/ES2020, Optional chaining allows us to safely access deeply nested properties without our code blowing up when a property is missing.
Let's say you're checking if your friend has a bag, and inside the bag, there's a pencil case, and inside that, a pen.
But what if they don't even have a bag? ๐คท
Usually, checking bag.pencilCase.pen
would give an error if the bag doesn't exist.
Optional chaining helps by saying:
"If the bag is there, check inside. If it's missing, just stop and return undefined. Don't throw an error."
Example:
let backpack = null;
console.log(backpack?.pencilCase?.pen); // undefined, no error
Without ?.
, this would crash. With ?.
, Javascript just says:
"Well, there's no backpack, so let's stop here." and return undefined
.
Without ?.
console.log(backpack.pencilCase.pen);
// Execution stops throwing "TypeError: Cannot read properties of null"
๐ก๏ธ What It Solves:
Before optional chaining, we had to check every level manually:
let pen; // Declare pen outside the if blocks to ensure scope
if (backpack) {
if (backpack.pencilCase) {
if (backpack.pencilCase.pen) {
pen = backpack.pencilCase.pen;
} else {
// Handle case where pencilCase exists but pen is missing
pen = null; // Or some default value
}
} else {
// Handle case where backpack exists but pencilCase is missing
pen = null; // Or some default value
}
} else {
// Handle case where backpack is null or undefined
pen = null; // Or some default value
}
One other way to handle is.
let pen;
if (backpack && backpack.pencilCase && backpack.pencilCase.pen) {
pen = backpack.pencilCase.pen;
} else {
pen = null; // Or some default value
}
Now it's one clean line:
let pen = backpack?.pencilCase?.pen || null;
โ ๏ธ Gotcha:
If we overuse this, we might accidentally ignore scenarios where something shouldn't be missing.
Use it to avoid errors, but still validate your data where needed.
โ Real-world Example Using All Three
let user = {}; // No profile available
let name = user?.profile?.name ?? "Guest";
console.log(`Hello, ${name}`); // "Hello, Guest"
This example:
Safely checks if the profile exists (
?.
).Falls back to
"Guest"
if name isundefined
(??
).Could spread new data into the user object (
...
), if needed.
๐ฏ Don't Stress If This Feels Like a Lot
We'll use these operators naturally as we build objects, handle API data, and work with forms.
For now, just know:
โ๏ธ ...
spreads and gathers things.
โ๏ธ ??
saves us from accidentally replacing 0
or false
.
โ๏ธ ?.
keeps our code from crashing when something's missing.
We'll unleash them fully as we go.
๐ Operator Precedence & Associativity
๐ฏ What is Operator Precedence?
Operator precedence determines which operation occurs first when multiple operators are present in the same expression.
Example:
2 + 3 * 4; // 14
Even though the +
comes first when we read it left to right, Javascript does the multiplication (*
) first โ because *
has higher precedence than +
.
So, the steps are:
3 * 4 = 12
2 + 12 = 14
That's why the answer is 14
.
๐ What is Associativity?
Associativity decides what happens when two operators have the same precedence.
It tells Javascript whether to solve things left-to-right or right-to-left.
Example (left-to-right associativity):
20 / 5 * 2;
Both /
and *
have the same precedence, so Javascript solves them left-to-right:
20 / 5 = 4
4 * 2 = 8
Example (right-to-left associativity for assignment):
let a = b = 5;
This actually runs as:
b = 5
then a = b
โ ๏ธ Why This Matters
If we don't understand precedence and associativity, we might get the wrong results in complex expressions.
Example:
2 + 3 * 4 ** 2;
// Javascript does: 4 ** 2 = 16, then 3 * 16 = 48, then 2 + 48 = 50
โ๏ธ Pro Tip:
When in doubt, use parentheses to make your logic clear and control the order:
(2 + 3) * 4; // 20
// without parentheses
2 + 3 * 4 // 14
Much easier for both Javascript and us humans to understand at a glance.
๐ก๏ธ Javascript Operator Precedence & Associativity (Simplified)
Operator(s) | Description | Precedence | Associativity |
() | Grouping (parentheses) | Highest | Left to Right |
?. , [] , . | Optional chaining, property access | 20 | Left to Right |
new (with arguments) | Constructor calls | 19 | Right to Left |
++ , -- (postfix) | Post-increment/decrement | 18 | Left to Right |
! , ~ , + , - , typeof , void , delete , await , ++ , -- (prefix) | Unary operators | 17 | Right to Left |
** | Exponentiation | 16 | Right to Left |
* , / , % | Multiplication, division, modulo | 15 | Left to Right |
+ , - | Addition, subtraction | 14 | Left to Right |
<< , >> , >>> | Bitwise shifts | 13 | Left to Right |
< , <= , > , >= , in , instanceof | Comparisons | 12 | Left to Right |
== , != , === , !== | Equality | 11 | Left to Right |
& | Bitwise AND | 10 | Left to Right |
^ | Bitwise XOR | 9 | Left to Right |
` | ` | Bitwise OR | 8 |
&& | Logical AND | 7 | Left to Right |
` | ` | Logical OR | |
?? | Nullish coalescing | 5 | Left to Right |
? : | Ternary conditional | 4 | Right to Left |
= , += , -= , *= , /= , %= , **= , etc. | Assignments | 3 | Right to Left |
yield , yield * | Yield | 2 | Right to Left |
, | Comma operator | Lowest | Left to Right |
โ ๏ธ Things That Often Surprise Us:
**
exponentiation is right-to-left, unlike most arithmetic.Assignments (
=
) happen right-to-left โ meaning the right side evaluates first.?.
and??
are higher precedence than||
and &&, but lower than arithmetic.Use parentheses
()
whenever things feel confusing.
โ๏ธ We won't memorise all of this โ we'll feel it naturally as we write more code.
But it's worth glancing at when something behaves unexpectedly.
๐ง Common Mistakes to Avoid
Even seasoned developers occasionally slip up on these. Here are the classics:
Using
==
instead of===
:==
does type coercion (and can give weird results). Stick with===
unless you mean to allow loose comparison.Relying on truthy/falsy without thinking:
Javascript treats some values like
0
,""
,null
,undefined
, andfalse
in surprising ways. We'll cover this properly soon โ no worries.Overloading one line with too many operators:
Just because Javascript lets us do it all in one line doesn't mean we should. Split it up. Your future self will thank you.
๐ Note:
Things like truthy/falsy behaviour and ==
vs ===
comparisons are some of Javascript's most famous quirks.
We'll dive deeper into these right after we finish covering control flow โ stay tuned!
๐งผ Best Practices for Clear Code
When we use operators the right way, our code becomes easier to read, debug, and maintain. Here are some tips we follow to keep our logic clean:
โ๏ธ Stick with ===
/ !==
:
Avoid the loose == and != unless you absolutely need type coercion (which is rare). Safer and more predictable.
โ๏ธ Use template literals for strings:
They make your code cleaner and save you from having to juggle too many +
signs when building dynamic messages.
โ๏ธ Break long expressions into steps:
Don't try to be clever by writing everything on one line.
One step at a time = easier for humans to read.
โ๏ธ Group operations with parentheses (()
):
Even if Javascript knows the order, adding parentheses makes it clear to anyone reading your code. Clarity > Cleverness.
โ๏ธ Favour short-circuiting (&&
, ||
) where it makes sense:
But don't turn your code into a puzzle. If the logic gets confusing, write it out clearly instead.
Great code isn't just about making things work โ it's about making things clear. That's what these practices help us do.
Javascript operators are essential โ they help us perform math, compare values, manage flow, and build dynamic expressions. When we use them wisely, our code becomes clean, readable, and much easier to debug.
We've only scratched the surface of what Javascript can do with operators. Up next, we'll dive into Control Flow โ where we'll finally give those conditions (if
, else
, and switch
) the spotlight they deserve.
Let's keep building our Javascript toolbox, one awesome feature at a time. Stay tuned! ๐
Subscribe to my newsletter
Read articles from Sangy K directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Sangy K
Sangy K
An Introvert Coder | Trying to be a Full-stack Developer | A Wannabe Content Creator