JavaScript Operators: A Comprehensive Guide for Beginners

Hello! I'm a dedicated software developer with a passion for coding and a belief in technology's impact on our world. My programming journey started a few years ago and has reshaped my career and mindset. I love tackling complex problems and creating efficient code. My skills cover various languages and technologies like JavaScript, Angular, ReactJS, NodeJs, and Go Lang. I stay updated on industry trends and enjoy learning new tools. Outside of coding, I cherish small routines that enhance my workday, like sipping tea, which fuels my creativity and concentration. Whether debugging or brainstorming, it helps me focus. When I'm not coding, I engage with fellow developers. I value teamwork and enjoy mentoring newcomers and sharing my knowledge to help them grow. Additionally, I explore the blend of technology and creativity through projects that incorporate art and data visualization. This keeps my perspective fresh and my passion alive. I'm always seeking new challenges, from open-source contributions to hackathons and exploring AI. Software development is more than a job for me—it's a passion that drives continuous learning and innovation.
Last updated: September 2026 — added nullish coalescing, optional chaining, logical assignment, exponentiation, Object.is, the remaining unary operators, spread, and a precedence table.
Welcome to our in-depth guide on JavaScript operators, designed to equip beginners with a clear understanding of these fundamental components of the language. Whether diving into web development for the first time or seeking a refresher, this article will provide an in-depth look at JavaScript operators and their various applications.
Introduction to JavaScript Operators
In programming, operators are symbols or keywords that enable us to perform various operations on values. JavaScript, a versatile and widely used programming language, boasts a rich set of operators that play a pivotal role in crafting dynamic and interactive web applications. By grasping the different types of operators, you'll gain the ability to manipulate data, make decisions, and perform many tasks.
Arithmetic Operators: Crunching Numbers with Ease
When it comes to performing mathematical calculations in JavaScript, arithmetic operators take center stage. These operators allow you to perform addition, subtraction, multiplication, division, and more on numeric values. The most common arithmetic operators include:
Addition (+): Combines two values, resulting in their sum.
Example:
5 + 3results in8, while"Hello" + "World"results in"HelloWorld".Subtraction (-): Subtracts the RHS value from the LHS value.
Example:
10 - 3equals7.Multiplication (*): Multiplies two values, yielding a product.
Example:
4 * 6equals24.Division (/): Divides the left-hand side value by the right-hand side value.
Example:
15 / 3equals5.Modulus (%): Returns the remainder after dividing the LHS value by the RHS value.
Example:
17 % 5equals2.Exponentiation (**): Raises the LHS value to the power of the RHS value. Added in ES2016 as a replacement for
Math.pow().Example:
2 ** 10equals1024.
A note on +
+ is really two operators wearing one symbol. With numbers it adds; if either side is a string it concatenates. That is why the order of a chain changes the answer:
1 + 2 + "3"; // "33" 1+2 is 3, then 3 + "3" concatenates
"1" + 2 + 3; // "123" a string from the first step onward
A note on **
Exponentiation is right-associative, unlike every other arithmetic operator:
2 ** 3 ** 2; // 512 — this is 2 ** (3 ** 2), not (2 ** 3) ** 2
And it refuses to sit next to a unary minus without parentheses:
-2 ** 2; // SyntaxError
(-2) ** 2; // 4
-(2 ** 2); // -4
That is deliberate. -2 ** 2 is genuinely ambiguous, so instead of picking an interpretation, the language makes you say which one you meant.
Assignment Operators: Giving Values a Purpose
Assignment operators are all about giving values purpose and direction. They allow you to assign values to variables, making it easier to store and manipulate data. The simple and widely used assignment operator (=) enables you to assign a value to a variable. For instance, let age = 15; assigns the value 15 to the variable age.
Equal (=): Assigns a value to a variable.
Example:
x = 10assigns the value 10 to the variable x.Add and Assign (+=): Adds a value to a variable and assigns the result.
Example:
x += 5is equivalent tox = x + 5.Subtract and Assign (-=): Subtracts a value from a variable and assigns the result.
Example:
x -= 3is equivalent tox = x - 3.Multiply and Assign (*=): Multiplies a variable by a value and assigns the result.
Example:
x *= 2is equivalent tox = x * 2.Divide and Assign (/=): Divides a variable by a value and assigns the result.
Example:
x /= 4is equivalent tox = x / 4.Modulus and Assign (%=): Calculates the modulus and assigns the result.
Example:
x %= 3is equivalent tox = x % 3.
Logical assignment (ES2021)
ES2021 added three more assignment operators. These work differently from the ones above: they only assign conditionally.
let a = null;
a ??= "default"; // assigns — a is null
// a is now "default"
let b = 0;
b ||= 50; // assigns — 0 is falsy
// b is now 50
let c = 0;
c ??= 50; // does NOT assign — 0 is not null/undefined
// c is still 0
??= is the one you will reach for most. It reads as "set this only if it has no value yet":
function configure(options) {
options.retries ??= 3;
options.timeout ??= 5000;
return options;
}
configure({ retries: 0 }); // { retries: 0, timeout: 5000 }
Notice retries: 0 survives. With ||= it would have been overwritten by 3 — which is exactly the bug this operator exists to prevent.
There is one more difference from += and friends. x += 1 always writes to x. The logical ones skip the write entirely when the condition is not met, which matters when the target has a setter or is being watched.
Comparison Operators: Making Sense of Differences
Comparison operators are your go-to tools for making sense of how different values relate to each other. These operators facilitate comparisons and return logical values, such as true or false. Some of the essential comparison operators include:
Equal (==): Checks if two values are equal, converting types first if they differ.
Example:
5 == 5evaluates totrue, and so does"5" == 5.Strict Equal (===): Compares both value and data type for equality.
Example:
5 === "5"evaluates tofalse.Not Equal (!=): Determines if two values are not equal, with the same type conversion as
==.Example:
10 != 5evaluates totrue.Strict Not Equal (!==): Determines if two values differ in value or type.
Example:
"5" !== 5evaluates totrue.Greater Than (>): Compares whether one value is larger than another.
Example:
8 > 3evaluates totrue.Less Than (<): Compares whether one value is smaller than another.
Example:
2 < 7evaluates totrue.Greater Than or Equal (>=): Checks if the first value is greater than or equal to the second.
Example:
10 >= 10evaluates totrue.Less Than or Equal (<=): Checks if the first value is less than or equal to the second.
Example:
5 <= 3evaluates tofalse.
Use ===, not ==
The conversions == performs produce results nobody wants:
0 == ""; // true
"0" == false; // true
[] == false; // true
null == undefined;// true
=== does none of that. The one place == earns its keep is value == null, which is a short way of asking "is this null or undefined?" — nothing else in the language is loosely equal to null.
When === is not strict enough
=== gets two cases wrong:
NaN === NaN; // false — NaN is not equal to itself
0 === -0; // true — but these are different values
Object.is is === with both corrected:
Object.is(NaN, NaN); // true
Object.is(0, -0); // false
For everyday comparison, keep using ===. Reach for Object.is when writing something generic — a deep-equality helper, a change detector, a memoisation cache — where "did this value actually change?" needs the exact answer.
Logical Operators: Unleashing the Power of Logic
Logical operators allow you to combine multiple conditions and evaluate complex expressions. These operators are vital for making decisions in your code. The three primary logical operators are:
AND (&&): Returns
trueif both conditions are true.Example:
(x > 5) && (y < 10)evaluates to true only ifxis greater than5andyis less than10.OR (||): Returns
trueif at least one of the conditions is true.Example:
(a > 10) || (b < 5)evaluates to true if eitherais greater than10orbis less than5.NOT (!): Flips the boolean value of a condition.
Example:
!(x > 3)evaluates totrueifxis not greater than3.
&& and || short-circuit — they stop as soon as the answer is known, so the right side may never run at all:
false && expensiveCheck(); // expensiveCheck() never runs
true || expensiveCheck(); // never runs either
They also return one of the operands, not a boolean. a || b gives you a if a is truthy, otherwise b. That is what makes || usable as a fallback — and what leads directly to the next operator.
Nullish Coalescing (??)
?? returns its left side unless that side is null or undefined, in which case it returns the right side.
It looks like a duplicate of ||. It is not, and the difference bites:
const settings = { volume: 0, username: "" };
settings.volume || 50; // 50 <-- wrong, 0 is a valid volume
settings.volume ?? 50; // 0 <-- correct
settings.username || "Anonymous"; // "Anonymous"
settings.username ?? "Anonymous"; // ""
|| falls back on any falsy value, so 0, "" and false all trigger the default even though they are legitimate values. ?? falls back only on null and undefined.
The rule: if 0, "" or false are valid values for that variable, use ??. If every falsy value should be replaced, || is correct.
One syntax rule. You cannot mix ?? with || or && without parentheses:
a || b ?? c // SyntaxError
(a || b) ?? c // fine
a || (b ?? c) // fine
That is deliberate. The two have different fallback rules, so the language refuses to guess.
Optional Chaining (?.)
Reading a property off null or undefined throws:
const user = { profile: { name: "Ganesh" } };
user.profile.name; // "Ganesh"
user.address.city; // TypeError: Cannot read properties of undefined
?. short-circuits the whole chain and gives back undefined instead:
user.address?.city; // undefined
There are three forms, one for each way we reach into a value:
obj?.prop // optional property access
obj?.[key] // optional bracket access
fn?.(args) // optional invocation
user.profile?.["name"]; // "Ganesh"
user.getName?.(); // undefined — no crash, the method does not exist
That last form is useful for optional callbacks:
function saveUser(data, onSuccess) {
// ... save ...
onSuccess?.(data); // runs only if a callback was passed
}
saveUser({ name: "Ganesh" }); // no callback, no crash
?. guards only against null and undefined — any other error still throws. And the short-circuit covers the rest of the chain: in a?.b.c.d, if a is null then b, c and d are never evaluated.
It pairs naturally with ??:
const city = user.address?.city ?? "Unknown";
One caution: do not sprinkle ?. everywhere out of habit. If a value should always be there, let the code throw. A loud crash at the real cause beats a silent undefined that surfaces three functions later.
Bitwise Operators
Bitwise operators convert their operands to 32-bit integers and work on the individual bits. You will meet them in flags and permission masks, colour manipulation, and low-level code — rarely in everyday application work.
AND (&): Performs a bitwise AND operation between two numbers.
Example:
5 & 3results in1.OR (|): Performs a bitwise OR operation between two numbers.
Example:
5 | 3results in7.XOR (^): Performs a bitwise XOR operation between two numbers.
Example:
5 ^ 3results in6.NOT (~): Performs a bitwise NOT operation on a single number.
Example:
~5results in-6.Left Shift (<<): Shifts the bits of a number to the left.
Example:
4 << 2results in16.Right Shift (>>): Shifts the bits of a number to the right, keeping the sign.
Example:
16 >> 2results in4, and-16 >> 2results in-4.Unsigned Right Shift (>>>): Shifts right and fills from the left with zeros, ignoring the sign.
Example:
-16 >>> 2results in1073741820.
Unary Operators: Operating on a Single Operand
Unary operators operate on a single operand. The increment (++) and decrement (--) operators are the most commonly used examples. They increase or decrease the value of a variable by one.
Increment (++): Increments the value by one.
Decrement (--): Decrements the value by one.
Prefix vs postfix
Both can go before or after the variable, and the position changes what the expression returns — not what the variable ends up as.
let a = 1;
a++; // returns 1, then a becomes 2
console.log(a); // 2
let i = 5;
console.log(i++); // 5 — returns the old value, then increments
console.log(i); // 6
console.log(++i); // 7 — increments first, then returns
The variable is 2 either way in the first example. The difference only shows when you use the result:
const arr = [1, 2, 3];
let j = 0;
arr[j++]; // 1 — reads index 0, THEN j becomes 1
arr[j]; // 2
Use postfix on its own line and you never have to think about this. Use it inside a larger expression and you do.
The other unary operators
++ and -- are not the only ones. These four come up constantly:
typeof 42; // "number"
typeof undefined; // "undefined"
typeof null; // "object" <-- a bug from 1995, kept for compatibility
typeof (() => {}); // "function"
[] instanceof Array; // true — is this constructor's prototype in the chain?
"a" in { a: 1 }; // true — does this key exist on the object?
const o = { a: 1 };
delete o.a; // true, and o is now {}
typeof is the only operator that does not throw on an undeclared variable, which makes it the safe way to check whether something exists at all:
typeof somethingUndeclared; // "undefined" — no ReferenceError
There is also void, which evaluates its operand and returns undefined. You will see it in old href="javascript:void(0)" links and almost nowhere else.
Spread and Rest (...)
The same three dots do opposite things depending on where they appear.
Spread expands an iterable into individual values:
Math.max(...[3, 9, 1]); // 9
[...[1, 2], ...[3]]; // [1, 2, 3]
{ ...{ a: 1 }, b: 2 }; // { a: 1, b: 2 }
Rest collects several values into one array, in a parameter list or a destructuring pattern:
function sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
}
sum(1, 2, 3); // 6
const [first, ...others] = [1, 2, 3];
// first is 1, others is [2, 3]
Spread happens at a call site or inside a literal. Rest happens where a name is being bound. That is the whole distinction.
Ternary Operator: A Concise Conditional Choice
The ternary operator is a concise way to make quick decisions in your code. It's a shorthand version of an if-else statement. The syntax is:
condition ? value_if_true : value_if_false
const age = 18;
const status = age >= 18 ? "adult" : "minor";
console.log(status); // "adult"
Because it is an expression, it produces a value — so it works in places a statement cannot go, such as inside a template literal or a JSX attribute:
`You are an ${age >= 18 ? "adult" : "minor"}.`
It is the only operator in JavaScript that takes three operands, which is where the name comes from.
Keep them shallow. Nested ternaries are technically legal and almost always harder to read than the if statement they replaced.
Precedence and Associativity
When an expression has more than one operator, precedence decides which runs first. This is where most operator bugs live.
The order, highest to lowest. You do not need to memorise this — you need to know it exists, and to recognise the rows that catch people:
| Precedence | Operators | Associativity |
|---|---|---|
| Highest | () grouping |
— |
. ?. [] new with args, function call |
left to right | |
++ -- (postfix) |
— | |
! ~ + - (unary), ++ -- (prefix), typeof void delete await |
right to left | |
** |
right to left | |
* / % |
left to right | |
+ - |
left to right | |
<< >> >>> |
left to right | |
< <= > >= in instanceof |
left to right | |
== != === !== |
left to right | |
& then ^ then | |
left to right | |
&& |
left to right | |
|| then ?? |
left to right | |
| Lowest | ? :, then = += &&= ??= and other assignments |
right to left |
Three rows earn their reputation.
Unary beats arithmetic:
typeof 1 + 1; // "number1"
typeof 1 runs first, giving "number", and then + 1 concatenates. What was probably meant is typeof (1 + 1).
+ is left-associative and does double duty, so the same operands give different answers depending on order:
1 + 2 + "3"; // "33"
"1" + 2 + 3; // "123"
Comparison beats equality, which makes chained comparison a trap:
3 > 2 > 1; // false
3 > 2 is true, then true > 1 becomes 1 > 1, which is false. JavaScript has no chained comparison — write 3 > 2 && 2 > 1.
The practical rule: if you had to stop and think about the order, add parentheses. They cost nothing at runtime and they save the next person reading the code — which is usually you, six months later.
Conclusion
JavaScript operators are the building blocks of expressive and functional code. By understanding and mastering these operators, you'll gain the power to manipulate data, make decisions, and create dynamic applications.
This guide covered arithmetic, assignment, comparison, logical, bitwise, unary, spread and rest, and ternary operators — plus the ES2020 and ES2021 additions (??, ?., &&=, ||=, ??=) that changed how modern JavaScript is written, and the precedence rules that decide what runs first.
Next in the series: JavaScript Statements, where we look at how to control the flow of a program.
Hope you like it, if yes ❤️ like & 📤share.
Thanks for your time.
Happy coding….





