completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Logical operators

Learn JavaScript's &&, ||, and ! operators: truthy and falsy decisions, short-circuit evaluation, operand return values, guard patterns, and safe defaults.

By the end, you can
  • 01
    Read logical checksPredict what &&, ||, and ! give for truthy and falsy values.
  • 02
    Spot skipped codeKnow when JavaScript stops early and skips the right side.
  • 03
    Use guards and defaultsWrite them without being surprised by 0, '', null, or undefined.
  • 04
    Name checks clearlyRead a check with several parts out loud, and pick an if statement when the visitor needs to be told what is missing.

Three operators, one idea

Code often needs to ask small yes-or-no questions. For example: "Do we have both things we need?" "Do we have at least one value we can use?" "Is this not true?" JavaScript has three logical operators for these questions:

  • && means "and".
  • || means "or".
  • ! means "not".

They use truthy and falsy values from Booleans & truthiness. A truthy value counts as "yes" in a check. A falsy value counts as "no". There are exactly eight falsy values:

  • false, 0, -0, and 0n
  • "", the empty string
  • null, undefined, and NaN

Every other value is truthy. So "Ada", 10, and even an empty list [] are truthy.

The values on each side of an operator are called its operands. In a && b, this lesson calls a the left value and b the right value.

The main idea

A logical operator checks whether a value is truthy or falsy. && and || can stop early and skip the right value. They give back one of the two values itself. Only ! always gives back true or false.

Real-life analogy&& is a security door

Picture a door that needs a badge and a PIN. If you have no badge, the guard does not ask for your PIN. The missing badge already decides the answer: you can't go in.

In real life: The guard asks for your badge
In JavaScript: JavaScript checks the left value of &&
In real life: Then the guard asks for your PIN
In JavaScript: JavaScript checks the right value of &&
In real life: No badge? The guard stops right away
In JavaScript: A falsy left value stops && early
In real life: Badge and PIN? You can go in
In JavaScript: Both values must be truthy

In this lesson you will:

  • step through code where a function call is skipped,
  • try different values and see exactly what comes back,
  • use two common patterns, called guards and defaults,
  • see why 0 || 10 gives 10, and how the next lesson's ?? fixes it.

&&, ||, and !

TABLES

Start with plain true and false. Then the three operators are easy to read:

  • a && b is true only when both are true.
  • a || b is true when at least one is true.
  • !a flips the value: true becomes false, and false becomes true.

The table below shows the same rules with examples. Read each row as: "this code gives true when …".

When &&, ||, and ! give true, using only true and false
When you writeWhen it gives trueExamples
a && bOnly when a and b are both true.true && true → true; true && false → false
a || bWhen a is true, or b is true, or both are.false || true → true; false || false → false
!aWhen a is falsy.!false → true; !true → false

Real code often uses values that are not true or false, like names and numbers. Then JavaScript does two things:

  1. It checks whether the value is truthy or falsy, to decide what to do.
  2. It gives back the value itself, not true or false.

That is why "cat" && 10 gives 10, not true.

Left: "cat" && 10. "cat" is truthy, so you get back the right value, 10. Right: 0 || "Guest". 0 is falsy, so you get back the right value, "Guest". Neither gives true."cat" && 10"cat"10truthygiven back10not true0 || "Guest"0"Guest"falsygiven back"Guest"not true
&& and || check whether each value is truthy or falsy. Then they give back one of the two values themselves. Here both give back the right value: 10 and "Guest", not true.

Boolean(value) is a different tool. It turns any value into true or false. The next table compares it with the operators. Read each row from left to right: the code, what JavaScript checks, and what you get back.

Boolean() turns a value into true or false; && and || give back a value
When you writeWhat JavaScript checksWhat you get back
Boolean("cat")The string is truthy.true, because Boolean() always gives true or false.
"cat" && 10The left value is truthy, so it moves on to the right value.10, the right value itself.
0 || "Guest"The left value is falsy, so it moves on to the right value."Guest", the right value itself.
!"cat"The string is truthy, and ! flips that.false, because ! always gives true or false.
Three checks with true and falsePop out in the code editor (opens in a new tab)JavaScript
const hasTicket = true;const hasId = false; console.log(hasTicket && hasId);console.log(hasTicket || hasId);console.log(!hasId);

Line 1 says the visitor has a ticket. Line 2 says they have no ID. Then:

  • Line 4 prints false. && needs both checks to pass, and the ID check fails.
  • Line 5 prints true. || needs only one check to pass, and the ticket check passes.
  • Line 6 prints true. ! flips hasId, which is false.

Give true-or-false variables names that sound like a question, such as hasTicket and isSignedIn. Then you can read hasTicket && hasId out loud: "has a ticket and has an ID". Clear names make a check much easier to get right.

What &&, ||, and ! give backPop out in the code editor (opens in a new tab)JavaScript
console.log("Asha" && "ready");console.log(0 && "ready");console.log("Asha" || "Guest");console.log("" || "Guest");console.log(!"tea");console.log(!!"tea");

Line by line:

  1. "Asha" is truthy, so && gives back the right value. It prints ready.
  2. 0 is falsy, so && stops and gives back 0.
  3. "Asha" is truthy, so || stops and gives back "Asha".
  4. "" is falsy, so || moves on and gives back "Guest".
  5. "tea" is truthy, and ! flips it, so this prints false.
  6. !! flips twice, so it prints true. Two flips turn any value into true or false.

Notice the pattern. && and || work the opposite way round. && stops at a falsy value. || stops at a truthy value.

Some empty-looking values are truthyPop out in the code editor (opens in a new tab)JavaScript
console.log(Boolean([]));console.log([] && "tea");console.log([] || "Guest");console.log("0" && 3);console.log(NaN || 5);

Line by line:

  1. An empty list, [], is still truthy. So Boolean([]) prints true.
  2. Because [] is truthy, && moves on and prints tea.
  3. Because [] is truthy, || stops and gives back the list itself, not "Guest".
  4. The string "0" has one character in it, so it is truthy. && moves on and prints 3.
  5. NaN is falsy, so || moves on and prints 5.
Turn a value into true or false only when you need to

Often you want the value itself, as in name || "Guest". Use Boolean(value) or !!value only when you need exactly true or false, for example to store a yes-or-no answer in a variable.

Short-circuit: stopping early

STEP THROUGH

Short-circuit means JavaScript stops as soon as it knows the answer. Then the right value is never worked out at all.

  • For &&, a falsy left value already means "no". JavaScript stops.
  • For ||, a truthy left value already means "yes". JavaScript stops.

This matters when the right side does something, like call a function, read a property, or print a message. If JavaScript stops early, that never happens.

Real-life analogy|| is a keyring

Picture trying keys on a keyring. The first key that opens the lock wins. Once the door is open, you don't try the other keys.

In real life: Try the front-door key
In JavaScript: JavaScript checks the left value
In real life: It opens the door? Stop trying keys
In JavaScript: A truthy left value stops || early
In real life: It doesn't open? Try the next key
In JavaScript: A falsy left value makes JavaScript check the right value
Watch JavaScript skip the right side
Step 0 of 4Ready
Your turn: follow the blue line

Predict which function runs. Then step through the real run: a skipped function has no step at all.

Running in
  1. script
Next: line 8
function checkBadge() {  console.log("badge checked");  return "";}function checkPin() {  console.log("PIN checked");  return "pin-ok";const result = checkBadge() && checkPin();console.log(result);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Choose which line to step through

A new choice starts the replay again. Predict which function will never run.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Look at the function that was skipped. It printed nothing, gave back nothing, and has no step in the replay. JavaScript really didn't run it. That's why isLoggedIn && showDashboard() calls showDashboard() only when isLoggedIn is truthy.

A skipped call saves nothingPop out in the code editor (opens in a new tab)JavaScript
let savedTimes = 0;function saveCart() {  savedTimes += 1;  console.log("cart saved");} false && saveCart();true && saveCart();console.log(savedTimes);

Lines 1 to 5 set up a counter, savedTimes, and a saveCart() function. Each call adds 1 to the counter (+= 1 means "add 1"). Then:

  • Line 7 does not call saveCart(), because the left value is false.
  • Line 8 does call it, because the left value is true.
  • Line 9 prints 1. That shows only one save happened.
Left: false && saveCart(). The left value is false, so JavaScript stops and saveCart() is skipped. Right: true && saveCart(). The left value is true, so JavaScript goes on and saveCart() runs.false && saveCart()falsesaveCart()falsy: stopskippedtrue && saveCart()truesaveCart()truthy: go onruns
&& looks at the left value first. When it is falsy, JavaScript stops there and never calls saveCart(). When it is truthy, JavaScript goes on to the right and the call runs.

So never put a step that must always happen on the right side of && or ||. It may be skipped.

Does the right side run?
  • "badge" && checkPin()
  • 0 && saveScore()
  • "Ada" || "Guest"
  • "" || "Guest"
  • user && user.name
  • null && user.name
Try it yourself
0 of 6 correct

Sort each line: does JavaScript work out the right value, or skip it?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

They give back a value, not true or false

INTERACTIVE

This rule surprises many beginners. && and || give back the value that decided the answer. They give back true or false only when that value already was true or false. Experienced developers use this rule to write short code. Here are the exact rules:

  • a && b gives back a if a is falsy. Otherwise it gives back b.
  • a || b gives back a if a is truthy. Otherwise it gives back b.
  • !a always gives back true or false. !!a flips twice, so it is a common way to turn any value into exactly true or false.

In the playground below, pick a left value, an operator, and a right value. Line 3 of the code prints what you get back. Line 4 prints the same answer turned into true or false.

What do && and || give back?
const left = "cat";const right = 10;console.log(left && right);console.log(Boolean(left && right));console.log(!left);console.log(!!left);
What you get"cat" && 10
Value given back
10
Boolean(result)
true
!left
false
!!left
true
Try it yourself

The line gives back 10. Boolean(result) is true. !left is false, and !!left is true.

Pick truthy and falsy values. The first line shows that && and || give back one of the two values. The second line shows that value turned into true or false.

Which operator goes first

STEP THROUGH

When one line mixes several operators and has no brackets, JavaScript needs a rule for which part to work out first. That rule is called precedence. For the logical operators, the order is:

  1. ! goes first.
  2. Then &&.
  3. Then ||.

So JavaScript reads true || false && false as true || (false && false). That gives true.

true || false && false, read the way JavaScript reads it: true || (false && false). Step 1: the && part, false && false, gives false. Step 2: true || false gives true.true||(false && false)true1. && goes firstit gives false2. then ||true || false
&& goes before ||, so JavaScript reads true || false && false as if the && part had brackets around it. Step 1 gives false. Step 2, true || false, gives true.
Step through which operator goes first
Step 0 of 4Ready
Your turn: follow the blue line

Step through the four lines to see which operator goes first, and one example of De Morgan's law.

Running in
  1. script
Next: line 1
console.log(true || false && false);console.log((true || false) && false);console.log(!(true && false) === (!true || !false));
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Brackets are often better than remembering the order. They show the next reader exactly what you meant. That reader may be you, a few months from now.

Which operator goes first: four examplesPop out in the code editor (opens in a new tab)JavaScript
console.log(!false && false);console.log(true || false && false);console.log((true || false) && false);console.log(!(true && false) === (!true || !false));

Line by line:

  1. ! goes first, so !false becomes true. Then true && false gives false.
  2. && goes before ||, so this is true || (false && false). It gives true.
  3. The brackets change the order. (true || false) is true, then true && false gives false.
  4. Both sides of === give true, so the line prints true. The next paragraph explains why.

Line 4 checks a rule called De Morgan's law. It shows how to move a ! inside brackets: !(a && b) means the same as !a || !b. In words: "not both passed" means the same as "the first failed, or the second failed".

Guards and defaults

INTERACTIVE

Stopping early gives you two useful patterns.

  • A guard goes on only when the left value is safe to use. user && user.name reads name only when user exists. If user is null, it stops, so it never tries to read name from null. That read would throw an error.
  • A default is a backup value. name || "Guest" gives "Guest" when name is falsy.
Guards and defaults in a small profile
A guard and two defaultsPop out in the code editor (opens in a new tab)JavaScript
const user = { name: "Ada" };const missingUser = null;console.log(user && user.name);console.log(missingUser && missingUser.name);console.log("" || "Guest");console.log(0 || 10);
What you getuser is null
user && user.name
null
name || "Guest"
"Guest"
count || 10
10
count ?? 10
0
Try it yourself

user && user.name gives null. name || "Guest" gives "Guest". count || 10 gives 10. The next lesson's count ?? 10 would give 0.

Switch user between an object and null, and change count. You'll see two useful patterns and one common bug: || treats 0 as missing. The next lesson's ?? uses the backup only for null or undefined.

Defaults with || have one catch. || treats every falsy value as missing. But sometimes 0 is a real value, like a count of 0 items. Then 0 || 10 wrongly gives 10.

The next lesson, Nullish coalescing & optional chaining, teaches two fixes:

  • ?? uses the backup only when the value is null or undefined. So 0 ?? 10 keeps 0.
  • ?. replaces long guard chains like user && user.address && user.address.city.
Left: count || 10. The box count holds 0. Because 0 is falsy, || gives 10 and the 0 is lost. Right: count ?? 10. The same box holds 0. ?? only replaces null or undefined, so it gives 0 and the 0 is kept.count || 100count100 is lostcount ?? 100count00 is kept
The box count holds a real 0. || treats every falsy value as missing, so it swaps the 0 for 10. ??, from the next lesson, only swaps null and undefined, so the 0 stays.
A guard and two defaultsPop out in the code editor (opens in a new tab)JavaScript
const user = { name: "Ada" };const missingUser = null;console.log(user && user.name);console.log(missingUser && missingUser.name);console.log("" || "Guest");console.log(0 || 10);

Lines 3 to 6, one at a time:

  • Line 3 prints Ada. user is an object, so it is truthy and the guard goes on to read the name.
  • Line 4 prints null. missingUser is null, so the guard stops before reading .name.
  • Line 5 prints Guest. The empty string is falsy, so the backup is used.
  • Line 6 prints 10. This is the catch: 0 is falsy, so it is replaced too.

Where you'll use this

You'll use logical operators whenever code needs a small decision inside one line. Here are common examples:

  • isLoggedIn && showDashboard() calls a function only when a check is truthy.
  • const label = nickname || "Guest"; shows a backup name when the nickname is empty.
  • const city = user && user.address && user.address.city; is an older guard chain. The next lesson replaces it with user?.address?.city.
  • if (isAdmin && hasToken) { ... } puts the check in an if statement, which is often easier to read.
Use an if when it reads more clearly

A call after && is handy for one tiny action. If the action needs several lines, an if statement is clearer and easier to debug. A later module covers if statements in full.

Check a name and a tick boxPop out in the code editor (opens in a new tab)JavaScript
const name = " Asha ";const acceptedTerms = true;const hasName = Boolean(name.trim()); console.log(hasName && acceptedTerms);

Line 3 uses name.trim(), which removes spaces from both ends of the text. What is left is "Asha". Boolean() then turns it into true, because it is not empty. Line 5 prints true, because the name has letters and the terms were accepted.

This only checks that the form is ready to send. It does not check everything. An email address, a password, and the server's own rules each need their own checks.

Step through a checkout check
Step 0 of 2Ready
Your turn: follow the blue line

Step through the checkout check. The replay follows this exact example step by step; it doesn't show what happens inside the JavaScript engine.

Running in
  1. script
Next: line 8
function showCheckout() {  console.log("checkout shown");  return "checkout";} const isSignedIn = true;const hasItems = true;console.log(page);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

The checkout example uses two true-or-false variables, so its result is easy to predict. Lines 6 and 7 say the visitor is signed in and has items in their cart. Line 8 reaches showCheckout() only after both checks pass. Line 9 prints checkout. If either variable were false, the function would be skipped and the line would give back false.

Three checks before an orderPop out in the code editor (opens in a new tab)JavaScript
const isSignedIn = true;const hasAddress = true;const hasItems = false; const canPlaceOrder = isSignedIn && hasAddress && hasItems;console.log(canPlaceOrder);

Lines 1 to 3 hold three yes-or-no answers, and each name reads like a question. Line 5 reads out loud as "is signed in and has an address and has items". The last value, hasItems, is false. So the whole && line gives back false, and line 6 prints false.

On a real order page, you could use a check like this to turn the order button on or off. If you also need to tell the visitor what is missing, use an if statement.

A backup name, and a count that must stay 0Pop out in the code editor (opens in a new tab)JavaScript
const nickname = "";const cartCount = 0; console.log(nickname || "Guest");console.log(cartCount || 10);console.log(cartCount ?? 10);
  • Line 4 prints Guest, because the nickname is empty.
  • Line 5 shows the catch. 0 is falsy, so cartCount || 10 prints 10.
  • Line 6 uses ??. It keeps the real count, so it prints 0.

Use ??, from the next lesson, whenever 0 or an empty string is a real value you want to keep.

Common misconceptions

  • "Logical operators always give true or false." Only ! does. && and || give back one of their two values.
  • "The right side always runs." JavaScript may stop early and skip it completely.
  • "A falsy value is useless." 0 and "" can be real data. Be careful with || defaults.
  • "!!x is the same as x." Both are truthy or both are falsy. But !!x is always exactly true or false.
  • "The order is obvious." Lines that mix && and || are easy to misread. Add brackets to make the order clear.

Practice exercises

6 EXERCISES
Exercise 1 · Warm-upPredict what each line prints

Predict all three printed values before you run the code.

Starter codePop out in the code editor (opens in a new tab)JavaScript
console.log("cat" && 10);
console.log(0 || "fallback");
console.log(!"");

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeFind the default bug

    Find the surprising printed value. Then look at the solution to see the fixed default.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const count = 0;
    console.log(count || 10);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeWrite a null guard

      Use && so the code reads name only when user exists.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const user = null;
      console.log(user && user.name);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFollow a short && chain

        This one line does the same job as an if inside another if. Follow the && chain from left to right and decide whether the function call runs.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const isLoggedIn = true;
        const hasPlan = true;
        function showDashboard() { console.log("dashboard"); }
        isLoggedIn && hasPlan && showDashboard();

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeUse De Morgan's law

          Write the same check without the outer ! and brackets. It must give the same answer.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const hasBadge = true;
          const hasPin = false;
          console.log(!(hasBadge && hasPin));
          console.log(!hasBadge || !hasPin);

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeProtect a checkout step

            A shop should open checkout only for a visitor who is signed in and has items in the cart. Predict whether the function runs in this code. Then change the first line to true and run it again to see checkout open.

            Starter codePop out in the code editor (opens in a new tab)JavaScript
            const isSignedIn = false;
            const hasItems = true;
            function showCheckout() { console.log("checkout shown"); }
            isSignedIn && hasItems && showCheckout();

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              8 QUESTIONS
              Logical operators quiz · 8 questionsScore: first tries count
              1. Question 1 of 8What does this line print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                console.log("cat" && "dog");

                Choose an answer to see the explanation.

              2. Question 2 of 8Which line skips the function call?

                Choose an answer to see the explanation.

              3. Question 3 of 8What does this default print when the name is empty?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                console.log("" || "Guest");

                Choose an answer to see the explanation.

              4. Question 4 of 8What does !0 print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                console.log(!0);

                Choose an answer to see the explanation.

              5. Question 5 of 8How does JavaScript read true || false && false?

                Choose an answer to see the explanation.

              6. Question 6 of 8count can be 0, and 0 should be kept. Which fix from the next lesson replaces count || 10?

                Choose an answer to see the explanation.

              7. Question 7 of 8Which line means the same as !(hasBadge && hasPin)?

                Choose an answer to see the explanation.

              8. Question 8 of 8How many times does saveCart run?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                let savedTimes = 0;
                function saveCart() { savedTimes += 1; }
                false && saveCart();
                true && saveCart();
                console.log(savedTimes);

                Choose an answer to see the explanation.

              Key takeaways

              • && needs both values to be truthy. It stops at the first falsy value.
              • || needs one truthy value. It stops at the first truthy value.
              • ! flips truthy and falsy, and always gives true or false.
              • && and || give back one of their values, not true or false.
              • Use guards and defaults with care. 0 and "" are falsy, but they are often real data.

              Logical operators check truthy and falsy values from left to right, stop as soon as they know the answer, and give back the value that decided it.

              Coming next: Nullish coalescing & optional chaining.

              CompleteFrontend Clear concepts. Working examples.