04 Conditionals and Boolean Logic
Learn how Boolean expressions, logical operators, and conditional structures let programs make reliable decisions, including how to handle boundaries, branch order, and testing.
From questions to Boolean results
Programs make decisions by evaluating conditions and selecting an appropriate path. A condition should express a clear question, such as whether a user is logged in, whether a score reaches a threshold, or whether a temperature is below zero.
A evaluates to exactly one of two logical values: true or false. Examples include age >= 18, password === "secret", and a Boolean variable such as isLoggedIn. A Boolean value can also be stored directly:
const hasPermission = true;
Keep assignment and comparison conceptually separate. Assignment changes a variable, whereas a comparison tests a relationship. In JavaScript, status === "ready" asks whether the values are strictly equal; status = "ready" assigns a value.
Takeaway: Begin every decision by stating precisely what question the program must answer.
Comparisons and boundary decisions
produce Boolean results by comparing two values. Common operators are:
===: strictly equal to!==: not strictly equal to>: greater than<: less than>=: greater than or equal to<=: less than or equal to
Boundary choices are especially important. If a program accepts a passing score with score >= 60, then a score of 60 passes. If it uses score > 60, the score must be greater than 60, so 60 does not pass.
Test a threshold deliberately with one value below it, the boundary itself, and one value above it. For a threshold of 60, the representative tests are 59, 60, and 61. This method exposes errors involving inclusive and exclusive ranges.
Takeaway: Choose by deciding explicitly whether the boundary belongs inside the accepted range.
Combining conditions with logic
combine or reverse Boolean expressions:
AND, written as
&&, is true only when both expressions are true.OR, written as
||, is true when at least one expression is true.NOT, written as
!, reverses a Boolean result.
Use AND when every requirement must be satisfied:
if (age >= 18 && hasID) { ... }
This allows entry only when the person is at least 18 and has identification. Use OR when any acceptable option is enough:
if (day === "Saturday" || day === "Sunday") { ... }
This identifies either weekend day. Use NOT when the desired condition is the opposite of an existing Boolean value:
if (!isClosed) { ... }
Translate a condition into ordinary language before coding. The expressions age >= 18 && hasID and age >= 18 || hasID have different requirements: the first needs both conditions, while the second needs only one.
When conditions are mixed, use parentheses to show the intended grouping. For example, (isMember || hasCoupon) && cartTotal >= 25 requires a membership or coupon and also requires a cart total of at least 25.
often use . With AND, a false first expression means the result is already false; with OR, a true first expression means the result is already true. This can avoid unnecessary work, but expressions with side effects should be used carefully.
Takeaway: Read compound conditions as sentences, and use parentheses when the grouping is important.
Choosing and ordering branches
A selects code based on a condition. The basic if form runs its block only when the condition is true:
if (temperature < 0) { print("Freezing"); }
Use if...else when exactly one of two alternatives should run:
if (balance >= price) { print("Purchase approved"); } else { print("Insufficient funds"); }
Use an else if chain for several mutually exclusive possibilities. Conditions are tested from top to bottom, and the first true branch runs. Therefore, broader tests should not hide narrower or higher-priority tests. For grades, test score >= 90 before score >= 60; otherwise, a score of 95 would match the lower threshold first.
Separate if statements are appropriate when multiple actions may run. If only one category should be selected, an else if chain is usually safer because the remaining branches are skipped after a match.
Takeaway: Select the conditional structure according to the number and relationship of possible outcomes, then order tests so that priority is explicit.
Nested decisions and exact-value choices
A places one decision inside another. For example, a program might first check isLoggedIn and then check isAdmin only for a logged-in user. This represents a dependent decision: the inner question matters only after the outer requirement is satisfied.
Nesting is useful, but excessive indentation can make code difficult to follow. Sometimes the same logic is clearer when conditions are combined, such as isLoggedIn && isAdmin. Another option is a , which handles an invalid or exceptional case early:
if (!isLoggedIn) { print("Ask the user to log in"); } else { showDashboard(); }
Use consistent indentation and braces. This reduces the risk of associating an else with the wrong if and makes the control flow visible.
For several exact values of one expression, a can be clearer than a long chain of comparisons:
switch (command) { case "start": startGame(); break; case "pause": pauseGame(); break; default: print("Unknown command"); }
The default branch handles unmatched values. In JavaScript, omitting break can produce , in which execution continues into the next case. This may be intentional, but accidental is a common source of unexpected behavior.
Use if...else if for ranges or complex Boolean conditions. A is best suited to exact-value choices such as commands or named states.
Takeaway: Prefer the clearest structure: dependent decisions may need nesting, simple early rejection may need a , and exact-value alternatives may suit switch.
A systematic method for reliable decisions
The provides a compact way to choose between two values:
const message = isOpen ? "Welcome" : "Closed";
When isOpen is true, message receives "Welcome"; otherwise, it receives "Closed". This form is useful for a simple value choice. Use a full if...else statement when each branch performs multiple actions or when the compact expression would be difficult to read.
A reliable problem-solving process is:
Identify all possible outcomes, such as approved, rejected, or pending.
Define exactly what must be true for each outcome.
Decide how boundary values such as 0, 60, or 100 should be handled.
Choose
if,if...else, anelse ifchain, nested conditionals, orswitch.Order tests by priority, placing narrower or more important tests before broader ones.
Test ordinary cases, boundary cases, invalid inputs, and cases where multiple conditions are true.
For example, free shipping may apply when an order total is at least 50 or the customer is a premium member:
if (orderTotal >= 50 || isPremiumMember) { shippingCost = 0; } else { shippingCost = 8; }
Useful tests include an order total of 49.99 for a non-member, an order total of 50 for a non-member, an order total of 20 for a premium member, and an order total of 50 for a premium member. Together, these cases check the boundary and both sides of the OR condition.
When a condition becomes difficult to verify, name its parts:
const meetsAgeRequirement = age >= 18;
const hasRequiredDocument = hasID || hasPassport;
if (meetsAgeRequirement && hasRequiredDocument) { print("Access allowed"); }
Meaningful Boolean names make the requirements easier to inspect and change.
Takeaway: Good conditional logic comes from precise requirements, deliberate boundaries, readable structure, and representative tests.