Real decisions rarely depend on a single question. A discount applies if the customer is a member and the basket is over £50. A form can be sent if the email is filled in or the phone number is. A button should show unless the user is already subscribed. JavaScript's logical operators, && (and), || (or) and ! (not), combine comparisons like these into one condition.
These operators have a second, very practical use. Because of something called short-circuit evaluation, they can pick between values, which is how JavaScript developers write default values and conditional actions in a single line.
This lesson also covers three short pieces of syntax you'll see in almost every modern codebase: the ternary operator (condition ? a : b) for compact if/else choices, the nullish coalescing operator (??) for safe defaults, and optional chaining (?.) for reading properties that might not exist without crashing your program.
And, or, not
| Operator | Name | Result is true when… | Example |
|---|---|---|---|
&& | AND | both sides are true | age >= 18 && hasTicket |
|| | OR | at least one side is true | isAdmin || isOwner |
! | NOT | the value is false | !isLoggedIn |
const age = 20;
const hasTicket = true;
console.log(age >= 18 && hasTicket); // true
console.log(age < 12 || age > 65); // false
console.log(!hasTicket); // false&& is evaluated before ||, just as multiplication comes before addition. Use brackets to make mixed conditions clear: (a && b) || c.
Short-circuit evaluation
JavaScript stops checking as soon as it knows the answer, and it returns the value that decided it, not always true or false:
a || breturnsaif it's truthy, otherwiseb.a && breturnsaif it's falsy, otherwiseb.
const name = "" || "Guest"; // "Guest"
const user = { name: "Sam" };
user && console.log(user.name); // logs "Sam"This is why || is often used for default values. The catch: || treats 0, "" and false as missing too, which isn't always what you want.
Nullish coalescing: ??
?? gives a default only when the left side is null or undefined:
| Expression | || result | ?? result |
|---|---|---|
0 ?? 10 vs 0 || 10 | 10 | 0 |
"" ?? "N/A" vs "" || "N/A" | "N/A" | "" |
null ?? 5 vs null || 5 | 5 | 5 |
undefined ?? 5 vs undefined || 5 | 5 | 5 |
Use ?? for settings such as volume, quantity or scores, where 0 is a real value. The assignment versions x ??= 5, x ||= 5 and x &&= y update a variable only when needed.
The ternary operator and optional chaining
The ternary operator is a one-line if/else that produces a value:
const label = stock > 0 ? "In stock" : "Sold out";Optional chaining ?. reads a property only if the thing before it exists. If it's null or undefined, the whole expression returns undefined instead of throwing an error:
const city = user.address?.city; // undefined if there's no address
const first = order.items?.[0]; // safe array access
user.notify?.(); // only calls notify if it existsCombine them: user.address?.city ?? "Unknown" reads safely and falls back to a default.
JavaScript Logical Operators Syntax
Logical operators
if (isMember && total > 50) { /* ... */ }
if (email || phone) { /* ... */ }
if (!done) { /* ... */ }
const volume = settings.volume ?? 50;
const label = count === 1 ? "item" : "items";
const city = user?.address?.city ?? "Unknown";
count ||= 1; // set if falsy
name ??= "Anon"; // set if null or undefinedJavaScript Logical Operators Summary
&&needs both sides true,||needs one, and!flips a value.- Logical operators short-circuit and return the deciding value.
??provides a default only fornullandundefined, so0and""are kept.condition ? a : bchooses between two values in one line.?.reads properties safely when something might be missing.
JavaScript Logical Operators Examples
Each example runs in the page. Edit the code and press Run to see what changes.
Combining conditions
Change the values on lines 1 to 3 and run again.
const age = 16;
const withAdult = true;
const isMember = false;
const canEnter = age >= 18 || withAdult;
const getsDiscount = isMember && age < 18;
const needsId = !isMember;
console.log("Can enter:", canEnter);
console.log("Gets discount:", getsDiscount);
console.log("Needs ID:", needsId);
console.log("Rule:", (age >= 18 || withAdult) && !needsId);Line-by-line explanation
- Line 5True if the visitor is an adult or comes with one.
- Line 6True only if both conditions are true.
- Line 7
!flipsfalsetotrue. - Line 12Brackets make the order of a mixed condition clear.
|| vs ?? for defaults
const settings = { volume: 0, nickname: "", theme: null };
const v1 = settings.volume || 50;
const v2 = settings.volume ?? 50;
console.log("volume with ||:", v1);
console.log("volume with ??:", v2);
const nick = settings.nickname ?? "Player";
const theme = settings.theme ?? "light";
console.log(`nickname: "${nick}"`, "theme:", theme);
let tries;
tries ??= 3;
console.log("tries:", tries);Line-by-line explanation
- Line 3
||replaces the valid value0with 50, which is a bug. - Line 4
??keeps 0 because it's not null or undefined. - Line 8The empty string is kept too.
- Line 9
nullgets the default. - Line 13
??=assigns only becausetriesis undefined.
Ternary and optional chaining with real data
const users = [
{ name: "Ana", address: { city: "Lisbon" }, cart: ["book", "pen"] },
{ name: "Ben" },
];
for (const user of users) {
const city = user.address?.city ?? "unknown city";
const count = user.cart?.length ?? 0;
const word = count === 1 ? "item" : "items";
console.log(`${user.name} from ${city} has ${count} ${word}`);
}Line-by-line explanation
- Line 7Ben has no address, so
?.returns undefined and??supplies a default. - Line 8The same pattern for a missing cart.
- Line 9The ternary picks the right word for the number.
Common JavaScript Logical Operators Mistakes
Using || when 0 is valid
const qty = input.quantity || 1; // a quantity of 0 becomes 1const qty = input.quantity ?? 1;Comparing against several values with ||
if (day === "Sat" || "Sun") { } // always true: "Sun" is truthyif (day === "Sat" || day === "Sun") { }
// or
if (["Sat", "Sun"].includes(day)) { }Optional chaining on the left of =
user?.profile.name = "Sam"; // SyntaxErrorif (user?.profile) user.profile.name = "Sam";?. is for reading, not assigning.
JavaScript Logical Operators in Real Projects
Where you'll use these
Form validation combines checks with &&. Feature flags use || to enable something for admins or beta users. React and other frameworks use && and the ternary operator to show or hide parts of the page. Data from APIs often has missing fields, so ?. and ?? appear everywhere data is displayed, for example product.reviews?.length ?? 0.
JavaScript Logical Operators Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Give size a default of 12 only if fontSize is null or undefined.
const fontSize = null;
const size = fontSize 12;
console.log(size);
?? only replaces null and undefined.What is logged?
console.log(0 || "a", 0 ?? "b", "" && "c");0 || "a" gives "a", 0 ?? "b" keeps 0, and "" && "c" stops at the falsy empty string.Which condition is true only when both values are true?
What does user.settings?.theme return if settings is undefined?
describe should return "no charge" for children under 5 or adults over 65, keep a discount of 0, and not crash when pass is missing. Fix the three bugs.
function describe(age, pass) {
const discount = pass.discount || 10;
const exempt = age < 5 && age > 65;
return exempt ? "no charge" : `pay, discount ${discount}`;
}
console.log(describe(3));
console.log(describe(30, { discount: 0 }));- Young children pay nothing
- Over 65s pay nothing
- A discount of 0 is kept
- No pass gives the default
JavaScript Logical Operators Quiz
5 questions to check what you learned.
What does
!truereturn?- true
- null
- An error
- false
What does
null || "x"return?- "x"
- null
- true
- false
When should you use ?? instead of ||?
- Always
- Never
- Only with strings
- When 0, "" or false are valid values that shouldn't be replaced
What does
x > 10 ? "big" : "small"return when x is 4?- "big"
- true
- "small"
- 4
What does
false && expensiveCheck()do?- Runs expensiveCheck and returns false
- Returns false without calling expensiveCheck
- Throws an error
- Returns undefined
JavaScript Logical Operators Challenge
Shipping label
Write shippingLabel(order) that returns a string:
- If
order.total >= 50andorder.country === "UK", the cost is0, otherwise4.99 - The city comes from
order.address?.city, defaulting to"Unknown"with?? - Return
`${city}: ${cost === 0 ? "Delivery included" : "£" + cost}`
function shippingLabel(order) {
}
console.log(shippingLabel({ total: 60, country: "UK", address: { city: "Leeds" } }));
console.log(shippingLabel({ total: 20, country: "UK" }));- Big UK orders ship at no cost
- Small orders pay
- Other countries pay
- Missing address is handled