javascript-today

Logical Operators

Logical operators allow you to combine multiple conditions and create more complex decision-making logic. They’re essential for writing realistic programs that check multiple criteria.

What are Logical Operators?

Operators that work with boolean values (true and false) to produce a new boolean result:

const age = 25;
const hasLicense = true;

// Can drive if both conditions are true
if (age >= 18 && hasLicense) {
  console.log('You can drive');
}

There are three main logical operators:

  • && (AND)
  • || (OR)
  • ! (NOT)

AND Operator (&&)

Returns true only if both conditions are true:

true && true     // true
true && false    // false
false && true    // false
false && false   // false

Basic Example

const age = 25;
const hasLicense = true;

if (age >= 18 && hasLicense) {
  console.log('You can drive');
} else {
  console.log('You cannot drive');
}
// Output: You can drive (both conditions are true)

Multiple Conditions

const age = 30;
const hasExperience = true;
const hasReferences = true;

if (age >= 25 && hasExperience && hasReferences) {
  console.log('Qualified for senior position');
}
// All three must be true

Practical Example: Form Validation

const username = 'alice';
const password = 'secret123';
const agreeToTerms = true;

if (username.length > 0 && password.length >= 8 && agreeToTerms) {
  console.log('Form is valid');
} else {
  console.log('Please fill all required fields');
}

OR Operator (||)

Returns true if at least one condition is true:

true || true     // true
true || false    // true
false || true    // true
false || false   // false

Basic Example

const isWeekend = false;
const isHoliday = true;

if (isWeekend || isHoliday) {
  console.log('No work today!');
} else {
  console.log('Time to work');
}
// Output: No work today! (at least one is true)

Multiple Conditions

const paymentMethod = 'credit card';

if (paymentMethod === 'credit card' || 
    paymentMethod === 'debit card' || 
    paymentMethod === 'PayPal') {
  console.log('Payment method accepted');
}

Practical Example: Access Control

const userRole = 'moderator';

if (userRole === 'admin' || userRole === 'moderator') {
  console.log('Access granted to moderation tools');
} else {
  console.log('Access denied');
}

NOT Operator (!)

Inverts a boolean value (true becomes false, false becomes true):

!true      // false
!false     // true

Basic Example

const isLoggedIn = false;

if (!isLoggedIn) {
  console.log('Please log in');
}
// Output: Please log in (not false = true)

Checking for False Conditions

const hasError = false;

if (!hasError) {
  console.log('Everything is working fine');
}

const isEmpty = false;

if (!isEmpty) {
  console.log('List has items');
}

Double NOT (!!)

Converts any value to boolean:

const username = 'alice';

!!username        // true (non-empty string)
!!'hello'         // true
!!''              // false (empty string)
!!0               // false
!!42              // true

Combining Operators

Mix AND, OR, and NOT for complex logic:

AND + OR

const age = 25;
const isStudent = false;
const isRetiree = false;

// Discount for students OR retirees
if (age >= 18 && (isStudent || isRetiree)) {
  console.log('Eligible for discount');
}

Parentheses matter! They control the order of evaluation:

// Without parentheses (wrong)
if (age >= 18 && isStudent || isRetiree) {
  // Interpreted as: (age >= 18 && isStudent) || isRetiree
}

// With parentheses (correct)
if (age >= 18 && (isStudent || isRetiree)) {
  // First checks (isStudent || isRetiree), then && with age
}

Complex Example: Ticket Pricing

const age = 15;
const isStudent = true;
const isWeekday = true;

// Full price for adults on weekends
// Discounted for children OR students on weekdays
if ((age < 18 || isStudent) && isWeekday) {
  console.log('Discounted ticket: $8');
} else {
  console.log('Full price ticket: $15');
}
// Output: Discounted ticket: $8

Short-Circuit Evaluation

Logical operators stop evaluating as soon as the result is determined:

AND (&&) Short-Circuit

Stops at first false:

false && console.log('This never runs');
// Nothing logged (stops at false)

true && console.log('This runs');
// Logs: This runs

const user = null;
const name = user && user.name;  // null (stops at null)
// Prevents error from accessing null.name

OR (||) Short-Circuit

Stops at first true:

true || console.log('This never runs');
// Nothing logged (stops at true)

false || console.log('This runs');
// Logs: This runs

Default Values with OR

const username = '';
const displayName = username || 'Guest';
console.log(displayName);  // 'Guest'

const userAge = 0;
const age = userAge || 18;
console.log(age);  // 18 (0 is falsy!)

Nullish Coalescing (??)

Returns right side only if left is null or undefined (not all falsy values):

const userAge = 0;

// With OR (wrong)
const age1 = userAge || 18;
console.log(age1);  // 18 (0 is falsy)

// With ?? (correct)
const age2 = userAge ?? 18;
console.log(age2);  // 0 (0 is not null/undefined)

const username = '';
const name1 = username || 'Guest';   // 'Guest' (empty string is falsy)
const name2 = username ?? 'Guest';   // '' (empty string is not null/undefined)

When to use:

  • Use || for default values with any falsy value
  • Use ?? for defaults only with null/undefined
const config = {
  timeout: 0,
  retries: null
};

const timeout = config.timeout || 5000;    // 5000 (0 is falsy)
const retries = config.retries ?? 3;        // 3 (null)

const timeoutCorrect = config.timeout ?? 5000;  // 0 (correct!)

Common Patterns

1. Range Checking

const score = 75;

if (score >= 60 && score <= 100) {
  console.log('Passing score');
}

const hour = 14;

if (hour >= 9 && hour < 17) {
  console.log('Office is open');
}

2. Multiple Valid Options

const fileType = 'jpg';

if (fileType === 'jpg' || fileType === 'png' || fileType === 'gif') {
  console.log('Valid image format');
}

// Or use array.includes()
if (['jpg', 'png', 'gif'].includes(fileType)) {
  console.log('Valid image format');
}

3. Validation with Multiple Checks

const email = 'user@example.com';
const password = 'myPassword123';

const isValidEmail = email.includes('@') && email.includes('.');
const isValidPassword = password.length >= 8;

if (isValidEmail && isValidPassword) {
  console.log('Registration successful');
} else {
  console.log('Invalid credentials');
}

4. Role-Based Access

const userRole = 'editor';
const resourceOwner = 'alice';
const currentUser = 'alice';

const canEdit = userRole === 'admin' || 
                userRole === 'editor' || 
                currentUser === resourceOwner;

if (canEdit) {
  console.log('Edit access granted');
}

5. Safe Property Access

const user = null;

// Without short-circuit (error!)
// const name = user.name;  // Error: Cannot read property 'name' of null

// With short-circuit (safe)
const name = user && user.name;
console.log(name);  // null (no error)

// Even safer with optional chaining
const name2 = user?.name;
console.log(name2);  // undefined

Operator Precedence

Order of evaluation (highest to lowest):

  1. ! (NOT)
  2. && (AND)
  3. || (OR)
// Without parentheses
true || false && false
// Evaluates as: true || (false && false)
// Result: true

// With parentheses for clarity
(true || false) && false
// Result: false

Best Practice: Use parentheses for clarity:

// Unclear
if (age >= 18 && hasLicense || hasPermit)

// Clear
if ((age >= 18 && hasLicense) || hasPermit)

Best Practices

DO:

  • Use parentheses for complex conditions
  • Keep conditions readable (split into variables if needed)
  • Use ?? for null/undefined defaults
  • Use short-circuit evaluation for safe access
// Good - clear and readable
const isEligible = age >= 18 && hasLicense;
const hasSpecialPermit = isStudent || isSenior;

if (isEligible || hasSpecialPermit) {
  grantAccess();
}

// Good - safe property access
const userName = user && user.name;

// Good - nullish coalescing for config
const timeout = config.timeout ?? 3000;

DON’T:

  • Write overly complex conditions in one line
  • Confuse || with ??
  • Forget that && and || return values, not just booleans
  • Ignore operator precedence
// Bad - too complex
if (age >= 18 && age < 65 && (isStudent || isTeacher) && !isSuspended || isAdmin)

// Bad - using || with 0 or empty string
const count = items.length || 0;  // Fine, but...
const count = items.length ?? 0;  // Better (explicit about null/undefined)

Truthy and Falsy with Logical Operators

Remember these values are falsy:

  • false, 0, '', null, undefined, NaN

Everything else is truthy:

'hello' && 'world'    // 'world' (both truthy, returns last)
0 && 'hello'          // 0 (first is falsy)
'hello' || 'world'    // 'hello' (first is truthy)
0 || 'hello'          // 'hello' (first is falsy)

// Practical use
const message = errorMessage || 'No errors';
const value = userInput || defaultValue;

Summary

Operator Meaning Example Returns
&& AND true && true true (both must be true)
|| OR true || false true (at least one true)
! NOT !true false (inverts)
?? Nullish null ?? 'default' 'default' (only for null/undefined)

Precedence: !&&||

Short-circuit:

  • && stops at first falsy
  • || stops at first truthy

Key Concepts:

  • Use && when all conditions must be true
  • Use || when any condition being true is enough
  • Use ! to check for false/opposite conditions
  • Use ?? for null/undefined defaults (not all falsy values)