A comparison checks one thing: is the score over 60? Real decisions usually depend on several things at once: is the player over 13 and has a parent approved? Is the user an admin or a moderator? Logical operators combine comparisons like these into one true/false answer.
Given let x = 6; and let y = 3;:
| Operator | Name | Example | Result |
|---|---|---|---|
| && | AND | x < 10 && y > 1 | true |
| || | OR | x === 5 || y === 5 | false |
| ! | NOT | !(x === y) | true |
| ?? | Nullish coalescing | null ?? "missing" | "missing" |
&& gives true only if both sides are true. If either side is false, the whole thing is false:
| true && true | true |
| true && false | false |
| false && true | false |
| false && false | false |
const age = 21;
const hasId = true;
if (age >= 18 && hasId) {
console.log("Entry allowed");
}
A very common use is checking that a number is inside a range:
let age = 15;
console.log(age >= 13 && age <= 19); // is it a teen?
13 ≤ age ≤ 19, and JavaScript will even run 13 <= age <= 19. But it is wrong. It works left to right: 13 <= age gives true or false, and then that boolean (as 1 or 0) is compared with 19, which is always true. So even age = 50 passes. Always write both comparisons in full, joined with &&.
|| gives true if at least one side is true. It is only false when both sides are false:
| true || true | true |
| true || false | true |
| false || true | true |
| false || false | false |
let x = 6;
let y = -3;
console.log(x > 0 || y > 0); // is either one positive?
const isAdmin = false;
const isMod = true;
if (isAdmin || isMod) {
console.log("Can moderate");
}
color is red or blue, you might write color === "red" || "blue". That does not mean what it says. It reads as (color === "red") || "blue", and "blue" is a non-empty string, which is truthy, so the check passes for every color. Repeat the comparison on both sides: color === "red" || color === "blue".
! flips a value: true becomes false and false becomes true. It takes only one operand, placed right after it:
console.log(!true);
console.log(!(5 === 8)); // 5 === 8 is false, so this is true
console.log(!0); // 0 is falsy, so ! gives true
console.log(!"hello"); // truthy, so ! gives false
let isLoading = false;
if (!isLoading) {
console.log("Content ready");
}
! is also the quick way to toggle a boolean, like a mute button: isMuted = !isMuted; flips it every time it runs.
! binds very tightly, to just the value right next to it. !score === 5 means (!score) === 5: it flips score to a boolean first, then compares that boolean with 5, which is always false. To negate a whole comparison, wrap it: !(score === 5), or just use score !== 5.
You can mix logical operators, but know their order: ! first, then &&, then ||. Just like * before +, && is done before ||:
console.log(true || false && false); // same as true || (false && false)
console.log((true || false) && false); // parentheses change the answer
Rather than relying on the order, use parentheses to show exactly what you mean:
const score = 88;
const attended = true;
const submitted = false;
// Must have a good score AND (attended OR submitted)
if (score >= 80 && (attended || submitted)) {
console.log("Passes criteria");
}
JavaScript stops working out a logical expression as soon as it knows the answer. This is called short-circuiting:
&&, if the left side is falsy, the answer must be false, so the right side is never even run||, if the left side is truthy, the answer must be true, so the right side is skippedThere is a second surprise hiding here. && and || do not always give back true or false. They give back whichever operand decided the answer:
console.log("" || "Guest"); // "" is falsy, so || moves on and returns "Guest"
console.log("Ana" || "Guest"); // "Ana" is truthy, so || returns "Ana"
console.log(0 && "hi"); // 0 is falsy, so && stops and returns 0
console.log("a" && "b"); // "a" is truthy, so && returns the right side
Inside an if this makes no difference, since the result is treated as truthy or falsy anyway. But it makes || a handy way to supply a default value:
let inputName = ""; // user left the box blank
let display = inputName || "Anonymous";
console.log(display);
The ?? operator (ES2020) returns the right side only when the left side is nullish: null or undefined. Otherwise it returns the left side:
let name = null;
let text = "missing";
console.log(name ?? text);
Why have ?? when || can already supply defaults? Because || treats every falsy value as missing, including 0, "", and false, which are often perfectly good values:
let volume = 0; // the user muted it on purpose
console.log(volume || 50); // 0 is falsy: replaced!
console.log(volume ?? 50); // 0 is not null/undefined: kept
let timeout = null; // never set
console.log(timeout ?? 3000);
?? when 0, "", or false are valid values that should be kept (a volume, a score, a setting). Use || when any empty or falsy value should be replaced (a blank name box).
You want to allow entry if someone is a member or has a ticket, but only if they are also over 12. Which condition is right?
isMember || hasTicket && age > 12(isMember || hasTicket) && age > 12isMember && hasTicket || age > 12isMember || (hasTicket || age > 12)&& requires the age too. A looks close, but && runs first, so it means isMember || (hasTicket && age > 12): a member of any age gets in.What does this log?
let score = 0;
console.log(score || "no score");
console.log(score ?? "no score");
no score then 0. || treats the falsy 0 as missing. ?? only replaces null or undefined, so the real score of 0 is kept.Why does if (color === "red" || "blue") run for every color?
(color === "red") || "blue". When the color is not red, || moves on to "blue", a non-empty string, which is truthy. Write color === "red" || color === "blue".&& that checks n is between 1 and 10. Test with 0, 5, and 11let age = 50; console.log(13 <= age <= 19); and explain the wrong answeron = !on; and log it each time0 || "default" with 0 ?? "default", then null || "default" with null ?? "default"