Dev, a Class 11 student in Bhopal, is learning JavaScript by building a small billing page for his father's kirana store. His father does small calculations all day: adding up a customer's bill, checking whether a packet is cheaper than last week, and deciding whether an order is big enough for free home delivery. Dev's first goal is simple. He reads two item prices from input boxes, ₹100 and ₹200, and adds them with +. The bill shows ₹100200. Nothing crashed, and the maths could not be easier, so why did JavaScript glue the two numbers together instead of adding them?
Every program, like every shop, runs on small calculations like these. In Chapter 3 we learned how variables store information. In this chapter we learn how to work with that information using operators. We will also look at type conversion, the reason "5" + 2 gives you "52" instead of 7. It confuses almost every beginner, so we'll take it slowly.
What Is an Operator?
An operator is a symbol that does something with one or more values. The values it works on are called operands.
let total = 10 + 5;
console.log(total); // 15
Here 10 and 5 are the operands, and + is the operator. The result, 15, is stored in total. That is the whole idea. Everything below is just a different symbol doing a different job.
Here is a quick map of the operators covered in this chapter, so you know what is coming:
| Group | Operators | What they do |
|---|---|---|
| Arithmetic | + - * / % ** ++ -- | Maths |
| Assignment | = += -= *= /= ??= ||= &&= | Store or update a value |
| Comparison | === !== == != > < >= <= | Compare two values, give true or false |
| Logical | && || ! | Combine or reverse conditions |
| Modern shortcuts | ? : ?? ?. | Short if-else, default values, safe property access |
Arithmetic Operators
These are the calculator inside JavaScript. Most of them work exactly like school maths.
let price1 = 100;
let price2 = 200;
console.log(price1 + price2); // 300 (addition)
console.log(1000 - 300); // 700 (subtraction)
console.log(5 * 100); // 500 (multiplication)
console.log(1000 / 4); // 250 (division: split a ₹1,000 bill among 4 friends)
Remainder (%)
The % operator gives the remainder after division. 10 % 3 is 1, because 3 goes into 10 three times with 1 left over.
Its most common use is checking whether a number is even or odd. An even number has no remainder when divided by 2:
let number = 8;
if (number % 2 === 0) {
console.log("Even number");
} else {
console.log("Odd number");
}
// Even number
One detail many tutorials skip: the result takes the sign of the left number. So -7 % 3 is -1, not 2. If you ever write an "is odd" check, use n % 2 !== 0 rather than n % 2 === 1, or it will fail for negative numbers.
Exponent (**)
The ** operator raises a number to a power. It does the same job as Math.pow() but is shorter to read.
console.log(2 ** 10); // 1024
console.log(2 ** 3 ** 2); // 512, because it is read as 2 ** (3 ** 2)
console.log((2 ** 3) ** 2); // 64
Notice that ** works from right to left. That surprises people, so add brackets whenever you chain it.
Increment (++) and Decrement (--)
++ adds 1 and -- subtracts 1. You will see them in counters, loops and stock updates:
let cartItems = 2;
cartItems++; // customer adds one more item
console.log(cartItems); // 3
let stock = 50;
stock--; // one unit sold
console.log(stock); // 49
There are two ways to write them, and they behave differently when used inside a bigger expression:
let i = 5;
console.log(i++, i); // 5 6 → returns the old value, then adds 1
let j = 5;
console.log(++j, j); // 6 6 → adds 1 first, then returns the new value
On a line by itself, i++ and ++i do exactly the same thing. The difference only shows up when you use the result, which is a good reason to keep them on their own line.
Assignment Operators
The plain = sign stores a value: let age = 25; means "put 25 into age". It does not mean "is equal to". That job belongs to comparison operators.
When you want to update a variable based on its current value, use the shortcut versions:
let score = 50;
score += 10; // same as score = score + 10 → 60
score -= 5; // same as score = score - 5 → 55
score *= 2; // same as score = score * 2 → 110
Logical Assignment (??=, ||=, &&=)
Newer JavaScript (ES2021) adds three assignment operators that only assign if a condition is met:
let a = null; a ??= 5; // assign only if a is null or undefined
let b = 0; b ||= 7; // assign if b is falsy (0, "", null, ...)
let c = 1; c &&= 9; // assign only if c is truthy
console.log(a, b, c); // 5 7 9
The one you will use most is ??= for default settings, for example options.timeout ??= 3000;. It keeps a valid value like 0, which ||= would wrongly overwrite.
Comparison Operators
Comparison operators ask a yes-or-no question and always answer with true or false.
| Operator | Meaning | Example | Result |
|---|---|---|---|
=== | Equal (same value and same type) | "10" === 10 | false |
!== | Not equal (value or type differs) | "10" !== 10 | true |
== | Loose equal (converts types first) | "10" == 10 | true |
> / < | Greater / less than | 20 > 10 | true |
>= / <= | Greater / less than or equal | 18 >= 18 | true |
Why === Is Safer Than ==
The loose == quietly converts both sides to the same type before comparing. That sounds helpful, but it produces results that are hard to predict:
console.log("10" == 10); // true
console.log(0 == ""); // true (an empty string becomes 0)
console.log("10" === 10); // false (string vs number)
The strict === never converts anything. If the types differ, the answer is simply false. Your code then does what it says, which is why professional teams use === almost everywhere.
A Trap: Comparing Strings
When both sides are strings, JavaScript compares them letter by letter, like words in a dictionary, not as numbers:
console.log(10 < 9); // false (numbers)
console.log("10" < "9"); // true ("1" comes before "9")
This bites people when sorting prices or ages that came from a form or a CSV file as text. Convert to numbers first (see "Type Conversion" below).
Logical Operators
Logical operators combine conditions, the same way you decide things in daily life: "I'll go if it's not raining and I finish work early."
- AND (
&&): true only if both sides are true. A login needs a valid email and a correct password. - OR (
||): true if at least one side is true. A page can be opened by an admin or a manager. - NOT (
!): flips the value.!trueisfalse.
let age = 25;
let verified = true;
console.log(age >= 18 && verified); // true
console.log(false || true); // true
console.log(!verified); // false
Short-Circuiting: Why && and || Return Values
Here is something beginners are rarely told. && and || stop as soon as they know the answer, and they return one of the actual values, not always true or false.
console.log("Hello" || "Guest"); // "Hello" (first value is truthy, so stop)
console.log("" || "Guest"); // "Guest" (first value is falsy, so take the second)
That is why you often see user && user.name. If user is empty, JavaScript never even tries to read .name, so there is no crash.
Which Operator Runs First?
When one line mixes operators, JavaScript follows a fixed order called precedence, just like BODMAS in school.
console.log(1 + 2 * 3); // 7 (multiply first)
console.log((1 + 2) * 3); // 9 (brackets win)
console.log(false && true || true); // true (&& runs before ||)
You don't need to memorise the full table. If you have to stop and think about the order, add brackets. The next person reading your code will thank you.
Putting It Together: A Free-Delivery Check
Let's combine what we have so far into something a real shopping site does. Say an online store gives free delivery when the order is at least ₹199 and the customer is either a member or spends ₹499 or more:
const cartTotal = 450;
const minOrder = 199;
const isMember = false;
const message =
cartTotal >= minOrder && (isMember || cartTotal >= 499)
? "Free delivery"
: "Delivery fee ₹40";
console.log(message); // Delivery fee ₹40
The customer has crossed the ₹199 minimum but is neither a member nor above ₹499, so the fee applies. Notice the brackets around the OR part. Without them the logic would read differently, which is exactly the precedence trap from the previous section.
Modern Shortcuts: ? :, ?? and ?.
The Ternary Operator (? :)
The ternary operator is a one-line if-else. It reads as "condition ? value if true : value if false".
let age = 20;
let status = age >= 18 ? "Adult" : "Minor";
console.log(status); // Adult
let stockLeft = 0;
console.log(stockLeft > 0 ? "In stock" : "Out of stock"); // Out of stock
Nullish Coalescing (??)
?? gives a fallback value only when the first value is null or undefined, in other words, genuinely missing.
let username = null;
console.log(username ?? "Guest"); // Guest
let discount = 0;
console.log(discount || 10); // 10 ← wrong: 0 was a real value
console.log(discount ?? 10); // 0 ← correct
A discount of zero is a real value, not a missing one. || treats 0 as "empty" and replaces it, while ?? keeps it. For default values, reach for ?? first.
Optional Chaining (?.)
Reading a property of something that doesn't exist crashes your program:
const user = {};
console.log(user.address.city);
// TypeError: Cannot read properties of undefined (reading 'city')
With ?., JavaScript stops at the first missing piece and returns undefined instead of crashing. Combined with ??, you get a clean fallback message:
console.log(user.address?.city); // undefined
const order = { customer: null };
console.log(order.customer?.name ?? "No customer found"); // No customer found
Type Conversion: Why "5" + 2 Is "52"
Type conversion means changing a value from one type to another, for example the text "100" into the number 100. JavaScript sometimes does this automatically (called coercion), and that is where most beginner bugs come from.
Automatic Conversion
The + operator has two jobs: adding numbers and joining text. If either side is a string, joining wins, so the number is turned into text:
console.log("5" + 2); // "52"
console.log("3" + 4 + 5); // "345" (text from the start)
console.log(3 + 4 + "5"); // "75" (3 + 4 = 7 first, then "7" + "5")
The other maths operators (- * /) only work with numbers, so they convert text into numbers instead:
console.log("10" - 5); // 5
console.log("5" * "2"); // 10
console.log("8" - "3"); // 5
So the same two values can give completely different answers depending on the operator. That inconsistency is the reason to convert types yourself.
The Classic Form Bug
Everything you read from an HTML input box, a URL or a query string arrives as a string, even if the user typed a number. Watch what happens with a quantity and a price:
const qty = "3"; // from <input>
const price = "129.99"; // from <input>
console.log(qty + price); // "3129.99" ← joined, not added!
console.log(qty * price); // 389.97 ← works, but only by luck
The fix is to convert the input once, right where you receive it, and then work only with numbers.
Explicit Conversion
| You want | Use | Example | Result |
|---|---|---|---|
| Number | Number(x) | Number("100") | 100 |
| Whole number from text | parseInt(x, 10) | parseInt("12abc", 10) | 12 |
| Decimal from text | parseFloat(x) | parseFloat("3.99 kg") | 3.99 |
| String | String(x) | String(100) | "100" |
| Boolean | Boolean(x) | Boolean("") | false |
Number() vs parseInt(): Which One?
They look similar but follow different rules. Number() is strict: the whole value must be a valid number, or you get NaN. parseInt() is lenient: it reads digits from the start and stops at the first character it doesn't understand.
console.log(Number("12abc")); // NaN
console.log(parseInt("12abc")); // 12
console.log(Number(" 42 ")); // 42 (spaces are ignored)
console.log(Number("")); // 0 ← careful!
console.log(parseInt("3.99")); // 3 (the decimal part is dropped)
console.log(parseInt("₹500")); // NaN (it can't start with ₹)
Watch the empty-string case. Number("") returns 0, so an empty form field can quietly turn into a valid-looking zero. Always check for empty input separately.
Dev learns this the hard way. After fixing his bill, he wraps every input in Number(), and the totals finally add up. Then his father leaves the quantity box empty for one item, and the bill shows ₹0 for that line instead of asking for a quantity, because Number("") is 0. Dev adds one check before converting: if the box is empty, show "Please enter a quantity" and stop there.
NaN: The Number That Isn't a Number
When a conversion fails, you get NaN ("Not a Number"). It has two odd properties: its type is "number", and it is not equal to anything, not even itself.
console.log(typeof NaN); // "number"
console.log(NaN === NaN); // false
console.log(Number.isNaN(NaN)); // true
console.log(isNaN("hello")); // true (converts first, misleading)
console.log(Number.isNaN("hello")); // false (no conversion, more honest)
To test for NaN, use Number.isNaN(). Never use === NaN, because it is always false.
Why 0.1 + 0.2 Is Not 0.3
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
This isn't a JavaScript bug. Computers store decimals in binary, and some numbers like 0.1 can't be stored exactly, the same way 1/3 can't be written exactly as a decimal (0.3333…). For showing a price, toFixed(2) is enough. For calculating money, a common habit is to work in paise (whole numbers) and divide by 100 only when displaying:
const priceInPaise = 12999; // ₹129.99
const total = 3 * priceInPaise; // 38997, exact
console.log((total / 100).toFixed(2)); // "389.97"
Truthy and Falsy Values
In an if condition, JavaScript converts any value to true or false. Only eight values become false. These are called falsy:
false, 0, -0, 0n, "", null, undefined, NaN
Everything else is truthy, including some that look false:
console.log(Boolean("0")); // true (a non-empty string)
console.log(Boolean("false")); // true (still a non-empty string)
console.log(Boolean(" ")); // true (a space is a character)
console.log(Boolean([])); // true (an empty array is an object)
So if (username) is a neat way to check that a name was typed. But if (quantity) will skip a valid quantity of 0. When zero is a meaningful value, compare explicitly: if (quantity !== undefined).
Common Mistakes (and the Fix)
| Mistake | What happens | Fix |
|---|---|---|
if (age == "18") | Works by accident and hides type problems | Convert, then use ===: Number(age) === 18 |
total = qty + price with form values | Text is joined: "3129.99" | Convert input with Number() as soon as you receive it |
discount || 10 | A real 0 becomes 10 | Use discount ?? 10 |
if (value === NaN) | Always false | Use Number.isNaN(value) |
parseInt(input) for a quantity box | "12abc" is accepted as 12 | Use Number() and reject NaN |
Number("") on an empty field | Becomes 0 silently | Check input.trim() === "" first |
My Opinion
Use === everywhere, and let a tool enforce it. Remembering the rules of == isn't worth it. Turn on ESLint's eqeqeq rule and the question never comes up in code review again.
Convert input at the door. Most type-conversion bugs in beginner code come from converting values in the middle of the logic, or in three different places. Convert once, where the data comes in (form, URL, API body), validate it, and everything after that deals only with clean numbers.
Prefer Number() over parseInt() for user input. parseInt forgives too much. If someone types "12abc" into a quantity box, I would rather reject it than quietly order 12 items.
Learn ?? and ?. early. Older tutorials teach || for defaults, but ?? is the better default today because it doesn't throw away valid zeros and empty strings.
Don't chase clever one-liners. A nested ternary or i++ buried inside an expression saves a line and costs the reader a minute. Readable code is the goal.
Try It Yourself
Predict the output before you run each line in your browser console (press F12 → Console). Answers are below.
1. console.log("3" + 4 + 5);
2. console.log(3 + 4 + "5");
3. console.log("8" - "3");
4. console.log(0 || "default");
5. console.log(0 ?? "default");
6. console.log(typeof NaN);
7. console.log(Boolean("false"));
8. console.log(2 ** 3 ** 2);
Show answers
1. "345" → a string at the start makes everything a join
2. "75" → 3 + 4 = 7 first, then joined with "5"
3. 5 → minus converts both strings to numbers
4. "default" → 0 is falsy, so || moves on
5. 0 → ?? only skips null and undefined
6. "number" → NaN is technically a number type
7. true → any non-empty string is truthy
8. 512 → ** runs right to left: 2 ** 9
Frequently Asked Questions
Should I ever use ==?
Very rarely. Some developers use value == null as a shortcut to check for both null and undefined. It works, but writing value === null || value === undefined (or using ??) is clearer for beginners and for code reviewers.
What is the difference between || and ???
|| falls back for any falsy value (0, "", false, null, undefined, NaN). ?? falls back only for null and undefined. Use ?? when 0 or an empty string are valid values.
Why does typeof NaN say "number"?
Because NaN is a special value inside the number type. It represents "the result of a maths operation that failed", so it lives with the other numbers.
Is +"42" the same as Number("42")?
Yes, the unary plus converts to a number using the same rules. Number() is easier to read, so prefer it in code others will maintain.
Key Takeaways
- Operators are symbols that work on values: arithmetic, assignment, comparison and logical.
%gives the remainder;**raises to a power and works right to left.- Always compare with
===and!==. &&and||short-circuit and return actual values, not justtrue/false.- Use
??for defaults and?.for properties that might not exist. +joins when either side is text;- * /convert to numbers. Convert input yourself withNumber().- Check for
NaNwithNumber.isNaN(), and handle money in paise to avoid decimal errors.
In the next chapter we use these comparisons and logical operators to make real decisions with if, else and switch.

0 Comments