Welcome back! 👋 In the previous lesson, you mastered the Number object. Now let's close out the JavaScript Objects section with the simplest — yet most powerful — value type in all of JavaScript: Booleans!
A boolean has just two values — true or false. But understanding how JavaScript treats every value as truthy or falsy, how logical operators really work, and how short-circuit evaluation powers real-world patterns like default values, conditional rendering, and guard clauses — that's where booleans become truly powerful.
This is a topic that every professional JavaScript developer uses every single day. Let's master it completely!
What is a Boolean?
A boolean is a data type with exactly two values: true and false. It's the foundation of all conditional logic — every if statement, every while loop, every ternary operator depends on a boolean result.
let isLoggedIn = true;
let isPremium = false;
let hasAccess = true;
console.log(typeof true); // "boolean"
console.log(typeof false); // "boolean"Creating Booleans
Boolean Literals (Most Common)
let active = true;
let disabled = false;From Comparisons
Every comparison expression evaluates to true or false.
console.log(5 > 3); // true
console.log(5 < 3); // false
console.log(5 === 5); // true
console.log(5 !== 5); // false
console.log(5 >= 5); // trueBoolean() Constructor — Explicit Conversion
console.log(Boolean(1)); // true
console.log(Boolean(0)); // false
console.log(Boolean("hello")); // true
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean([])); // true (empty array is truthy!)
console.log(Boolean({})); // true (empty object is truthy!)Double Negation !! — Concise Conversion
console.log(!!1); // true
console.log(!!0); // false
console.log(!!"hello"); // true
console.log(!!""); // false
console.log(!!null); // false
console.log(!![]); // true
// Same as Boolean() but shorter
let value = "Mihir";
console.log(!!value); // true — is value truthy?Truthy and Falsy Values
This is one of the most important concepts in JavaScript. Every value — not just booleans — has an inherent truthiness. When JavaScript needs a boolean (in an if, while, &&, ||, ternary), it coerces the value.
The 6 Falsy Values
These are the only 6 falsy values in JavaScript. Everything else is truthy.
// The complete list of falsy values:
if (false) console.log("false"); // ❌ falsy
if (0) console.log("0"); // ❌ falsy
if (-0) console.log("-0"); // ❌ falsy
if (0n) console.log("0n"); // ❌ falsy (BigInt zero)
if ("") console.log('""'); // ❌ falsy (empty string)
if (null) console.log("null"); // ❌ falsy
if (undefined) console.log("undefined"); // ❌ falsy
if (NaN) console.log("NaN"); // ❌ falsyEverything Else is Truthy
// All of these are TRUTHY — might surprise you!
if ("0") console.log('"0" is truthy'); // ✅ non-empty string
if ("false") console.log('"false" is truthy'); // ✅ non-empty string
if ([]) console.log('[] is truthy'); // ✅ empty array
if ({}) console.log('{} is truthy'); // ✅ empty object
if (1) console.log('1 is truthy'); // ✅
if (-1) console.log('-1 is truthy'); // ✅
if (Infinity) console.log('Infinity is truthy'); // ✅Truthy vs Falsy Quick Reference
| Value | Truthy/Falsy |
|---|---|
false | ❌ Falsy |
0, -0 | ❌ Falsy |
"" (empty string) | ❌ Falsy |
null | ❌ Falsy |
undefined | ❌ Falsy |
NaN | ❌ Falsy |
true | ✅ Truthy |
| Any non-zero number | ✅ Truthy |
| Any non-empty string | ✅ Truthy |
[] (empty array) | ✅ Truthy |
{} (empty object) | ✅ Truthy |
| Any function | ✅ Truthy |
Comparison Operators
Strict vs Loose Equality
// == (loose) — converts types before comparing
console.log(0 == false); // true — 0 and false both falsy
console.log("" == false); // true — both falsy
console.log(1 == true); // true — coercion
console.log("1" == 1); // true — string converted to number
console.log(null == undefined); // true — special case
// === (strict) — no type conversion
console.log(0 === false); // false — different types
console.log("" === false); // false — different types
console.log(1 === true); // false — different types
console.log("1" === 1); // false — different types
console.log(null === undefined); // false — different types
// ✅ Always use === in your codeComparison Operators
let a = 10, b = 20;
console.log(a > b); // false
console.log(a < b); // true
console.log(a >= 10); // true
console.log(a <= 9); // false
console.log(a === 10); // true
console.log(a !== b); // trueLogical Operators
&& — AND (Both must be true)
console.log(true && true); // true
console.log(true && false); // false
console.log(false && true); // false
console.log(false && false); // false
// Practical
let age = 25;
let hasId = true;
let canEnter = age >= 18 && hasId;
console.log(canEnter); // true|| — OR (At least one must be true)
console.log(true || true); // true
console.log(true || false); // true
console.log(false || true); // true
console.log(false || false); // false
// Practical
let isWeekend = false;
let isHoliday = true;
let isDayOff = isWeekend || isHoliday;
console.log(isDayOff); // true! — NOT (Flip the boolean)
console.log(!true); // false
console.log(!false); // true
console.log(!0); // true (0 is falsy)
console.log(!""); // true (empty string is falsy)
console.log(!"hi"); // false ("hi" is truthy)
console.log(!null); // true (null is falsy)
// Practical
let isLoggedIn = false;
if (!isLoggedIn) {
console.log("Please log in"); // runs
}Short-Circuit Evaluation
This is one of the most powerful and widely-used patterns in JavaScript. Logical operators don't just return true/false — they return one of their operands, and they stop evaluating as soon as the result is determined.
&& Returns the First Falsy Value (or Last Value)
// && stops and returns the first FALSY value it finds
// If all are truthy, it returns the LAST value
console.log(1 && 2 && 3); // 3 — all truthy, returns last
console.log(1 && 0 && 3); // 0 — 0 is falsy, stops here
console.log(false && "hello"); // false — false is falsy, stops here
console.log("hi" && "world"); // "world" — all truthy, returns last
console.log(null && "render me"); // null — null is falsy, stops here|| Returns the First Truthy Value (or Last Value)
// || stops and returns the first TRUTHY value it finds
// If all are falsy, it returns the LAST value
console.log(0 || "default"); // "default" — 0 is falsy, continues
console.log("" || "Guest"); // "Guest" — "" is falsy, continues
console.log(null || undefined || "fallback"); // "fallback"
console.log("Mihir" || "Guest"); // "Mihir" — truthy, stops here
console.log(0 || false || null); // null — all falsy, returns lastReal-World Patterns with Short-Circuit
Default Values with ||
let username = "";
let displayName = username || "Guest";
console.log(displayName); // "Guest" — username is falsy
let user = { name: "Mihir" };
let role = user.role || "viewer";
console.log(role); // "viewer" — user.role is undefined (falsy)Conditional Execution with &&
let isLoggedIn = true;
let userName = "Mihir";
// ✅ Only greet if logged in
isLoggedIn && console.log(`Welcome, ${userName}!`); // runs
let isAdmin = false;
isAdmin && console.log("Admin panel loaded"); // doesn't run
// ✅ Conditional rendering pattern (used heavily in React)
let cart = ["Laptop", "Mouse"];
let cartLabel = cart.length > 0 && `(${cart.length} items)`;
console.log(cartLabel); // "(2 items)"Guard Clauses with &&
function processUser(user) {
// ✅ Guard: only call methods if user exists
let name = user && user.name && user.name.toUpperCase();
console.log(name); // "MIHIR" or false/null/undefined
}
processUser({ name: "Mihir" }); // "MIHIR"
processUser(null); // null — no crash ✅Nullish Coalescing ??
The ?? operator returns the right-hand value only when the left-hand value is null or undefined — unlike || which triggers on any falsy value.
// || triggers on ANY falsy value (0, "", false, null, undefined)
console.log(0 || "default"); // "default" — 0 is falsy
console.log("" || "default"); // "default" — "" is falsy
console.log(false || "default"); // "default" — false is falsy
// ?? triggers ONLY on null or undefined
console.log(0 ?? "default"); // 0 — 0 is not null/undefined ✅
console.log("" ?? "default"); // "" — "" is not null/undefined ✅
console.log(false ?? "default"); // false — false is not null/undefined ✅
console.log(null ?? "default"); // "default" — null triggers ??
console.log(undefined ?? "default"); // "default" — undefined triggers ??When to Use || vs ??
let userScore = 0; // A real score of zero
// ❌ || treats 0 as falsy — shows wrong default
let display1 = userScore || "No score yet"; // "No score yet" — wrong!
// ✅ ?? only replaces null/undefined — 0 is preserved
let display2 = userScore ?? "No score yet"; // 0 — correct!
console.log(display1); // "No score yet" ❌
console.log(display2); // 0 ✅let settings = {
theme: "dark",
fontSize: 0, // intentionally 0
showTips: false, // intentionally false
username: null, // not set
};
// ✅ Use ?? for settings — preserves intentional 0 and false
let theme = settings.theme ?? "light"; // "dark"
let fontSize = settings.fontSize ?? 14; // 0 (not 14!)
let showTips = settings.showTips ?? true; // false (not true!)
let username = settings.username ?? "Guest"; // "Guest"
console.log({ theme, fontSize, showTips, username });
// { theme: "dark", fontSize: 0, showTips: false, username: "Guest" }Optional Chaining ?.
?. safely accesses nested properties — returning undefined instead of throwing a TypeError if any part of the chain is null or undefined.
let user = {
name: "Mihir",
address: {
city: "Mumbai",
},
};
// ❌ Without optional chaining — crashes if property missing
console.log(user.contact.phone); // TypeError!
// ✅ With optional chaining — returns undefined safely
console.log(user.contact?.phone); // undefined
console.log(user.address?.city); // "Mumbai"
console.log(user.address?.pin?.code); // undefinedWith Methods
let user = { name: "Mihir" };
// ❌ Crashes if greet doesn't exist
user.greet(); // TypeError!
// ✅ Safe method call
user.greet?.(); // undefined — no crash ✅Combined with ??
let user = { profile: null };
// ✅ Safe access + fallback
let city = user.profile?.address?.city ?? "Unknown";
console.log(city); // "Unknown" — safe and clean ✅
let user2 = { profile: { address: { city: "Mumbai" } } };
let city2 = user2.profile?.address?.city ?? "Unknown";
console.log(city2); // "Mumbai" ✅Boolean Methods
Boolean.prototype.toString()
console.log(true.toString()); // "true"
console.log(false.toString()); // "false"Boolean.prototype.valueOf()
let b = new Boolean(true);
console.log(b.valueOf()); // true
// ⚠️ Avoid new Boolean() — objects are always truthy!
let falsyBool = new Boolean(false);
if (falsyBool) {
console.log("This runs!"); // ✅ runs — object is truthy even if value is false!
}
// ✅ Use boolean literals — never new Boolean()
let correct = false;
if (!correct) {
console.log("This runs correctly!"); // ✅
}Real-World Examples
Example 1: Permission System
function checkPermission(user, action) {
let isLoggedIn = !!user;
let isActive = user?.active ?? false;
let isAdmin = user?.role === "admin";
let isOwner = user?.role === "owner";
let isPremium = user?.plan === "pro" || user?.plan === "enterprise";
let permissions = {
view: isLoggedIn && isActive,
create: isLoggedIn && isActive && isPremium,
edit: isLoggedIn && isActive && isPremium,
delete: isLoggedIn && isActive && (isAdmin || isOwner),
manageUsers: isLoggedIn && isActive && isAdmin,
};
return permissions[action] ?? false;
}
let users = [
{ name: "Mihir", role: "admin", plan: "enterprise", active: true },
{ name: "Priya", role: "member", plan: "pro", active: true },
{ name: "Rahul", role: "member", plan: "free", active: false },
null, // not logged in
];
let actions = ["view", "create", "delete", "manageUsers"];
users.forEach(user => {
let name = user?.name ?? "Guest";
let perms = actions
.filter(a => checkPermission(user, a))
.join(", ") || "none";
console.log(`${name.padEnd(8)}: ${perms}`);
});
// Output:
// Mihir : view, create, edit, delete, manageUsers
// Priya : view, create, edit
// Rahul : none (inactive)
// Guest : noneExample 2: Form Validator
function validateForm(data) {
let errors = {};
// Name — required, non-empty
if (!data.name || !data.name.trim()) {
errors.name = "Name is required";
}
// Email — required and contains @
if (!data.email) {
errors.email = "Email is required";
} else if (!data.email.includes("@") || !data.email.includes(".")) {
errors.email = "Enter a valid email";
}
// Age — must be a number and in range
if (data.age === null || data.age === undefined || data.age === "") {
errors.age = "Age is required";
} else if (!Number.isFinite(Number(data.age))) {
errors.age = "Age must be a number";
} else if (Number(data.age) < 18 || Number(data.age) > 100) {
errors.age = "Age must be between 18 and 100";
}
// Terms — must be accepted
if (!data.acceptedTerms) {
errors.terms = "You must accept the terms";
}
let isValid = Object.keys(errors).length === 0;
return { isValid, errors };
}
let formTests = [
{
name: "Mihir Shah", email: "mihir@example.com",
age: 25, acceptedTerms: true,
},
{
name: "", email: "not-an-email",
age: 15, acceptedTerms: false,
},
{
name: "Priya", email: "priya@example.com",
age: null, acceptedTerms: true,
},
];
formTests.forEach((data, i) => {
let { isValid, errors } = validateForm(data);
console.log(`\nForm ${i + 1}: ${isValid ? "✅ Valid" : "❌ Invalid"}`);
if (!isValid) {
Object.entries(errors).forEach(([field, msg]) => {
console.log(` ${field}: ${msg}`);
});
}
});Example 3: Feature Flags System
const featureFlags = {
darkMode: true,
betaEditor: false,
notifications: true,
aiSuggestions: false,
exportToPDF: true,
};
function isFeatureEnabled(feature, userPlan = "free") {
let flagEnabled = featureFlags[feature] ?? false;
let premiumOnly = ["betaEditor", "aiSuggestions", "exportToPDF"];
if (!flagEnabled) return false;
if (premiumOnly.includes(feature)) return userPlan === "pro" || userPlan === "enterprise";
return true;
}
let users = [
{ name: "Mihir", plan: "enterprise" },
{ name: "Priya", plan: "pro" },
{ name: "Rahul", plan: "free" },
];
let features = Object.keys(featureFlags);
users.forEach(({ name, plan }) => {
let enabled = features.filter(f => isFeatureEnabled(f, plan));
console.log(`${name} (${plan}): ${enabled.join(", ") || "none"}`);
});
// Output:
// Mihir (enterprise): darkMode, notifications, exportToPDF
// Priya (pro): darkMode, notifications, exportToPDF
// Rahul (free): darkMode, notificationsExample 4: Smart Defaults with ?? and ?.
// Simulating config loaded from an API (some values may be null/undefined)
let apiConfig = {
theme: "dark",
fontSize: 0, // intentionally 0 — use smallest font
showSidebar: false, // intentionally off
maxItems: null, // not set — use default
user: {
name: "Mihir",
preferences: null, // not set yet
},
};
function buildAppConfig(raw) {
return {
theme: raw.theme ?? "light",
fontSize: raw.fontSize ?? 14, // 0 preserved! ✅
showSidebar: raw.showSidebar ?? true, // false preserved! ✅
maxItems: raw.maxItems ?? 20, // null → 20 ✅
username: raw.user?.name ?? "Guest",
language: raw.user?.preferences?.language ?? "en",
timezone: raw.user?.preferences?.timezone ?? "Asia/Kolkata",
};
}
let config = buildAppConfig(apiConfig);
console.log("=== ⚙️ App Config ===");
for (let [key, val] of Object.entries(config)) {
console.log(` ${key.padEnd(14)}: ${val}`);
}
// Output:
// theme : dark
// fontSize : 0 ← preserved (not replaced with 14!)
// showSidebar : false ← preserved (not replaced with true!)
// maxItems : 20 ← null replaced with default
// username : Mihir
// language : en ← from default
// timezone : Asia/Kolkata ← from defaultCommon Mistakes
Mistake 1: Using == Instead of ===
// ❌ Loose equality causes unexpected coercions
console.log(0 == false); // true — dangerous!
console.log("" == false); // true — dangerous!
console.log(null == undefined); // true — unexpected
console.log("1" == 1); // true — string coerced to number
// ✅ Always use === — no surprises
console.log(0 === false); // false ✅
console.log("" === false); // false ✅
console.log(null === undefined); // false ✅Mistake 2: Empty Array/Object are Truthy
let items = [];
// ❌ Checking array truthiness — ALWAYS true, even when empty!
if (items) {
console.log("Has items"); // Always runs — [] is truthy! ❌
}
// ✅ Check length for arrays
if (items.length > 0) {
console.log("Has items");
}
if (items.length) { // also works — 0 is falsy
console.log("Has items");
}Mistake 3: Using || When You Should Use ??
let config = { timeout: 0, retries: 0, debug: false };
// ❌ || replaces ALL falsy values including intentional 0 and false
let timeout = config.timeout || 5000; // 5000 — wrong! 0 was intentional
let retries = config.retries || 3; // 3 — wrong! 0 was intentional
let debug = config.debug || true; // true — wrong! false was intentional
// ✅ ?? only replaces null/undefined
let timeout2 = config.timeout ?? 5000; // 0 ✅
let retries2 = config.retries ?? 3; // 0 ✅
let debug2 = config.debug ?? true; // false ✅Mistake 4: Double Negation Confusion
// ❌ Easy to mix up ! and !!
let value = "hello";
console.log(!value); // false — negates truthy string
console.log(!!value); // true — converts to boolean
// ✅ Use !! to convert any value to a boolean
let hasValue = !!value;
console.log(hasValue); // true ✅
// Practical: storing boolean from a string check
let searchTerm = "JavaScript";
let hasSearchTerm = !!searchTerm.trim();
console.log(hasSearchTerm); // true ✅Mistake 5: new Boolean() Always Truthy
// ❌ Boolean object — always truthy regardless of value!
let b = new Boolean(false);
if (b) {
console.log("This runs!"); // ✅ runs! Object is truthy even with false value
}
// ✅ Never use new Boolean() — use boolean literals or Boolean()
let correct = Boolean(false); // false primitive ✅
let literal = false; // false primitive ✅
if (!correct) console.log("Correctly false ✅");Practical Exercise
Create a file called boolean.js:
// 🎯 Boolean Practice
// 1. Truthy/Falsy explorer
console.log("=== ✅❌ Truthy / Falsy ===");
let testValues = [
true, false, 1, 0, -1, "", "0", "false",
null, undefined, NaN, [], {}, Infinity, -Infinity,
];
testValues.forEach(v => {
let label = JSON.stringify(v) ?? String(v);
console.log(`${String(label).padEnd(14)}: ${!!v ? "✅ Truthy" : "❌ Falsy"}`);
});
// 2. Short-circuit patterns
console.log("\n=== ⚡ Short-Circuit Patterns ===");
let users = [
{ name: "Mihir", role: "admin", active: true, score: 0 },
{ name: "Priya", role: "editor", active: true, score: 92 },
{ name: "Rahul", role: "viewer", active: false, score: null},
null,
];
users.forEach(user => {
let name = user?.name ?? "Guest";
let role = user?.role ?? "none";
let isActive = user?.active ?? false;
let score = user?.score ?? "N/A"; // ?? preserves 0!
let canEdit = !!(user && isActive && (role === "admin" || role === "editor"));
let status = isActive ? "🟢 Active" : "🔴 Inactive";
console.log(`${name.padEnd(8)} | ${role.padEnd(8)} | Score: ${String(score).padEnd(5)} | ${status} | Edit: ${canEdit ? "✅" : "❌"}`);
});
// 3. Config builder with ?? and ?.
console.log("\n=== ⚙️ Config Builder ===");
let rawConfigs = [
{ theme: "dark", fontSize: 0, notify: false, user: { name: "Mihir", prefs: { lang: "hi" } } },
{ theme: null, fontSize: null, notify: null, user: { name: "Priya", prefs: null } },
{ theme: "light", fontSize: 16, notify: true, user: null },
];
rawConfigs.forEach((raw, i) => {
let config = {
theme: raw.theme ?? "light",
fontSize: raw.fontSize ?? 14,
notify: raw.notify ?? true,
username: raw.user?.name ?? "Guest",
language: raw.user?.prefs?.lang ?? "en",
};
console.log(`Config ${i + 1}: theme=${config.theme}, fontSize=${config.fontSize}, notify=${config.notify}, user=${config.username}, lang=${config.language}`);
});
// 4. Permission checker
console.log("\n=== 🔐 Permission Checker ===");
function canDo(user, action) {
if (!user || !user.active) return false;
const rules = {
read: () => true,
write: () => user.role === "admin" || user.role === "editor",
delete: () => user.role === "admin",
export: () => user.plan === "pro" || user.plan === "enterprise",
};
return rules[action]?.() ?? false;
}
let testUsers = [
{ name: "Mihir", role: "admin", plan: "enterprise", active: true },
{ name: "Priya", role: "editor", plan: "pro", active: true },
{ name: "Rahul", role: "viewer", plan: "free", active: true },
{ name: "Sara", role: "admin", plan: "pro", active: false },
];
let testActions = ["read", "write", "delete", "export"];
testUsers.forEach(user => {
let allowed = testActions.filter(a => canDo(user, a)).join(", ") || "none";
console.log(`${user.name.padEnd(8)} [${user.role.padEnd(6)}] [${user.plan.padEnd(10)}] ${user.active ? "🟢" : "🔴"} → ${allowed}`);
});Run it:
node boolean.jsExpected Output:
=== ✅❌ Truthy / Falsy ===
true : ✅ Truthy
false : ❌ Falsy
1 : ✅ Truthy
0 : ❌ Falsy
-1 : ✅ Truthy
"" : ❌ Falsy
"0" : ✅ Truthy
"false" : ✅ Truthy
null : ❌ Falsy
undefined : ❌ Falsy
NaN : ❌ Falsy
[] : ✅ Truthy
{} : ✅ Truthy
Infinity : ✅ Truthy
-Infinity : ✅ Truthy
=== ⚡ Short-Circuit Patterns ===
Mihir | admin | Score: 0 | 🟢 Active | Edit: ✅
Priya | editor | Score: 92 | 🟢 Active | Edit: ✅
Rahul | viewer | Score: N/A | 🔴 Inactive | Edit: ❌
Guest | none | Score: N/A | 🔴 Inactive | Edit: ❌
=== ⚙️ Config Builder ===
Config 1: theme=dark, fontSize=0, notify=false, user=Mihir, lang=hi
Config 2: theme=light, fontSize=14, notify=true, user=Priya, lang=en
Config 3: theme=light, fontSize=16, notify=true, user=Guest, lang=en
=== 🔐 Permission Checker ===
Mihir [admin ] [enterprise] 🟢 → read, write, delete, export
Priya [editor] [pro ] 🟢 → read, write, export
Rahul [viewer] [free ] 🟢 → read
Sara [admin ] [pro ] 🔴 → noneKey Takeaways
Congratulations! 🎉 You now have a complete, deep understanding of booleans in JavaScript.
✅ Only 6 falsy values — false, 0, -0, "", null, undefined, NaN. Everything else is truthy — including [], {}, "0", and "false".
✅ Always use === — loose == causes type coercion surprises.
✅ && returns the first falsy value or the last value. Use for conditional execution and guard clauses.
✅ || returns the first truthy value or the last value. Use for fallbacks — but only when 0 and false should trigger the fallback.
✅ ?? (Nullish Coalescing) — only falls back for null or undefined. Preserves intentional 0 and false. Use for settings, config, and API data.
✅ ?. (Optional Chaining) — safely access nested properties. Returns undefined instead of crashing. Combine with ?? for clean default handling.
✅ !! — double negation converts any value to its boolean equivalent. Cleaner than Boolean().
✅ Never use new Boolean() — object wrappers are always truthy regardless of their value.
Best Practices
- ✅ Always use
===— never==exceptnull == undefinedif you need both - ✅ Use
??instead of||for default values when0,false, or""are valid values - ✅ Use
?.whenever accessing properties of potentially null/undefined objects - ✅ Use
!!for explicit boolean conversion —!!valuenotBoolean(value) - ✅ Check array emptiness with
.length— never with truthiness alone - ✅ Use
&&for conditional rendering and guard clauses — cleaner thanif - ✅ Combine
?.and??—user?.settings?.theme ?? "light"is a common real-world pattern - ✅ Never use
new Boolean()— always use boolean primitives
🎉 JavaScript Objects Section — Complete!
Amazing work! You've now mastered all four built-in JavaScript objects:
| Object | What You Learned |
|---|---|
| Date | Create, format, compare, and calculate with dates and times |
| Math | Rounding, random numbers, min/max, powers, roots, constants |
| Number | Parsing, formatting, NaN/Infinity checks, floating point fixes |
| Boolean | Truthy/falsy, &&/||/!, short-circuit, ??, ?. |
What's Next?
Next up, we're moving into DOM & BOM — where JavaScript meets the browser!
DOM & BOM — Learn to manipulate web pages, respond to user events, navigate browser history, and build truly interactive experiences!
Let's keep going! 💪