JavaScript Logical Operators
JavaScript logical operators are not just for combining finished booleans. They also short circuit, preserve original values, and power common fallback patterns. That is why && can guard a function call, || can accidentally erase 0, and ?? often says what people meant in the first place.
Use logical operators to model rules. Use them carefully when you are selecting values.
The three core logical operators
The core logical operators are:
&&means AND. The whole expression passes only when both sides pass.||means OR. The whole expression passes when at least one side passes.!means NOT. It flips a truthy or falsy value into a boolean opposite.
Most examples start with booleans:
true && true; // true
true && false; // false
true || false; // true
false || false; // false
!true; // false
!false; // true
In application code, each side is usually a comparison:
const age = 20;
const hasTicket = true;
const canEnter = age >= 18 && hasTicket === true;
console.log(canEnter); // true
The JavaScript comparison operators create the booleans. The logical operators combine them.
AND means every required rule passes
Use && when every condition is required:
const hasAccount = true;
const acceptedTerms = true;
const seatsRemaining = 2;
const canSignUp = hasAccount && acceptedTerms && seatsRemaining > 0;
console.log(canSignUp); // true
That reads as: has an account, accepted terms, and has a seat available.
If one part fails, the whole rule fails:
const hasAccount = true;
const acceptedTerms = false;
const seatsRemaining = 2;
const canSignUp = hasAccount && acceptedTerms && seatsRemaining > 0;
console.log(canSignUp); // false
For long rules, naming the smaller checks helps:
const hasSeat = seatsRemaining > 0;
const canSignUp = hasAccount && acceptedTerms && hasSeat;
That is not just prettier. It makes the business rule easier to audit.
OR means at least one path passes
Use || when more than one condition can allow the same outcome:
const isAdmin = false;
const isModerator = true;
const canReviewReports = isAdmin || isModerator;
console.log(canReviewReports); // true
|| is useful for alternate permission paths, fallback labels, and feature checks. The danger is using it when you really need every rule to pass:
const hasPaid = false;
const hasSeat = true;
const canJoin = hasPaid || hasSeat;
console.log(canJoin); // true
If the real rule is "paid and has a seat," || is wrong. This is the kind of bug that looks harmless until someone gets into a paid workshop with enthusiasm and zero payment. Charming energy, wrong invoice.
NOT flips the condition
The ! operator turns a truthy value into false and a falsy value into true:
const isLoggedIn = false;
if (!isLoggedIn) {
console.log("Please sign in");
}
It is often clearer to name the positive state and negate it at the use site:
const hasPermission = role === "admin";
if (!hasPermission) {
console.log("Access denied");
}
Avoid piling up negations:
if (!isNotAllowed) {
// Please do not make future-you decode this before coffee.
}
Rename the variable or rewrite the condition so the rule reads plainly.
Short circuit evaluation
Logical operators short circuit. They stop as soon as the final result is known.
With &&, a falsy left side stops the expression:
const user = null;
user && console.log(user.name);
The right side never runs because user is falsy. That is why the code does not crash by trying to read name from null.
With ||, a truthy left side stops the expression:
const displayName = "Ada" || "Guest";
console.log(displayName); // "Ada"
The fallback is ignored because the first value already passes.
Short circuiting is useful, but do not hide important actions inside clever expressions. If the main purpose is an action, an if statement is often easier to read.
Logical operators return values, not just booleans
This is the part that surprises beginners:
"Ada" && "Admin"; // "Admin"
"" && "Admin"; // ""
"Ada" || "Guest"; // "Ada"
"" || "Guest"; // "Guest"
&& returns the first falsy value it finds. If nothing is falsy, it returns the last value.
|| returns the first truthy value it finds. If nothing is truthy, it returns the last value.
That is why this works:
const savedName = "";
const displayName = savedName || "Guest";
console.log(displayName); // "Guest"
It is also why this can fail.
The fallback bug with 0
The || operator treats every falsy value as a reason to use the fallback:
const savedVolume = 0;
const volume = savedVolume || 50;
console.log(volume); // 50
If 0 means "muted," that code just ignored the user's choice. Small bug, loud result.
Use nullish coalescing when only null and undefined mean "missing":
const savedVolume = 0;
const volume = savedVolume ?? 50;
console.log(volume); // 0
Use || when any falsy value should fall back. Use ?? when only missing values should fall back.
For the full list of falsy values, see Truthy and Falsy Values in JavaScript.
Precedence can change the result
&& has higher precedence than ||:
console.log(false || true && false); // false
JavaScript reads that as:
console.log(false || (true && false)); // false
If you mean the OR part to happen first, use parentheses:
console.log((false || true) && false); // false
That example happens to produce the same final value. Here is one where grouping changes the result:
const isAdmin = true;
const isEditor = false;
const accountActive = false;
const canPublishA = isAdmin || isEditor && accountActive;
const canPublishB = (isAdmin || isEditor) && accountActive;
console.log(canPublishA); // true
console.log(canPublishB); // false
The first rule lets admins publish even when the account is inactive. The second rule requires the account to be active for everyone. Parentheses are not noise here. They are the rule.
Combine logical operators with ternaries carefully
A ternary is often clearer when you need one of two values:
const label = isPublished ? "Published" : "Draft";
&& is not a full ternary replacement:
const label = isPublished && "Published";
When isPublished is false, label becomes false, not "Draft". That may be fine for conditional rendering in some UI libraries, but it is a bad general-purpose label.
Use the JavaScript ternary operator when both branches need explicit values.
Key Takeaways
- Use
&&when every rule must pass, and||when any valid path can pass. !flips truthiness, but readable names matter more than clever negation.- Logical operators short circuit, so the right side may never run.
&&and||return original values, not automatic booleans.- Prefer
??over||when0,"", orfalseare valid saved values.