Javascript Double Question Mark (??) - A Guide

JavaScript developers, especially those transitioning from other languages, might encounter the double question mark (??) and wonder about its purpose. Is it just another operator? While it might seem simple, the double question mark, known as the Nullish Coalescing Operator, is a powerful tool for handling default values in a more precise way than its cousin, the logical OR operator.
In this guide, we'll unravel the JavaScript double question mark. We will explore its functionality, understand how it differs from the logical OR operator (||), and discover how it can be a valuable addition to your JavaScript coding techniques. Let's dive in and clarify the mystery of the double question mark!
What is the Double Question Mark (!!)?
In JavaScript, the double question mark (??) is known as the Nullish Coalescing Operator. Introduced in ES2020, it provides a concise way to handle default values specifically when dealing with null or undefined values. Unlike the logical OR operator (||) which deals with falsy values, ?? focuses solely on nullish values—null and undefined.
To fully appreciate ??, it's important to understand the concept of Nullishness and how it contrasts with Falsiness, especially when compared to the behavior of the Logical OR operator.
Nullishness vs. Falsiness
In JavaScript, both nullishness and falsiness are concepts that help determine default values, but they operate on different sets of values. Understanding their difference is key to using ?? and || effectively.
Nullish Values
As mentioned, in JavaScript, there are only two values that are considered nullish:
- null
- undefined
The ?? operator is specifically designed to work with these two values. It checks if a value is either null or undefined, and if it is, it provides a default value.
Falsy Values
We discussed falsy values in detail in our Double Exclamation Mark (!!) guide. To reiterate, JavaScript has eight falsy values:
- false
- 0 (numeric zero)
- -0 (numeric negative zero)
- "" (an empty string)
- null
- undefined
- NaN (Not a Number)
- 0n (BigInt zero)
The logical OR operator (||) treats all of these values as "false" when evaluating conditions or providing defaults. This is where the key difference from ?? lies, and why ?? can be more precise in certain scenarios.
How Double Question Mark (??) works?
The Nullish Coalescing Operator (??) is a binary operator, meaning it sits between two operands: a left-hand side (LHS) and a right-hand side (RHS). It evaluates the LHS and, if it is nullish (either null or undefined), it returns the RHS. Otherwise, it returns the LHS.
Here’s a breakdown of the logic:
- Evaluate LHS: JavaScript first evaluates the expression on the left-hand side of the ?? operator.
- Check for Nullishness: It then checks if the evaluated value is either null or undefined.
- Return Value:
- If the LHS is null or undefined, the operator returns the value of the RHS expression.
- If the LHS is not null or undefined (even if it’s falsy like 0 or ''), the operator returns the value of the LHS expression.
Let's look at some examples to illustrate this behavior:
javascriptlet name = null ?? "Guest User"; console.log("Name:", name); // Output: Name: Guest User (null is nullish, so default is used) let age = undefined ?? 25; console.log("Age:", age); // Output: Age: 25 (undefined is nullish, so default is used) let count = 0 ?? 100; console.log("Count:", count); // Output: Count: 0 (0 is NOT nullish, so 0 is used) let message = "" ?? "No message"; console.log("Message:", message); // Output: Message: (empty string is NOT nullish, so empty string is used) let active = false ?? true; console.log("Active:", active); // Output: Active: false (false is NOT nullish, so false is used) let existingValue = "Hello" ?? "Default"; console.log("Existing Value:", existingValue); // Output: Existing Value: Hello ( "Hello" is NOT nullish, so "Hello" is used)
These examples clearly show that ?? only kicks in when the value on the left is strictly null or undefined. This is a crucial distinction from the logical OR operator (||), which we will explore further.
Use Cases for Double Question Mark (??)
The Nullish Coalescing Operator (??) is particularly useful in scenarios where you want to provide default values, but only when a variable is truly missing a value (null or undefined) and not just falsy.
1. Providing Default Values More Precisely
When you need to assign a default value to a variable if it's null or undefined, ?? offers a more specific and often more desirable behavior than ||. Consider cases where falsy values like 0 or "" are valid and should not be replaced by defaults.
javascriptfunction displayValue(value) { const displayedValue = value ?? "Value not provided"; console.log("Displayed:", displayedValue); } displayValue(null); // Output: Displayed: Value not provided displayValue(undefined); // Output: Displayed: Value not provided displayValue(0); // Output: Displayed: 0 (0 is valid, not replaced) displayValue(""); // Output: Displayed: (empty string is valid, not replaced) displayValue("Hello"); // Output: Displayed: Hello
In this example, using ?? ensures that the default message is only used when value is explicitly missing (null or undefined), allowing 0 and "" to be displayed as intended, which would not be the case if we used ||.
2. Handling Optional Properties in Objects
When working with objects that may have optional properties, ?? is excellent for safely accessing these properties and providing defaults if they are not present (null or undefined).
javascriptconst user = { name: "John Doe", // age property is intentionally missing or could be set to null/undefined in some cases preferences: { theme: "dark", notifications: null // User explicitly disabled notifications } }; const userName = user.name ?? "Unknown User"; const userAge = user.age ?? "Age not specified"; const userTheme = user.preferences.theme ?? "light"; const notificationsEnabled = user.preferences.notifications ?? true; // Default to true if explicitly disabled with null/undefined console.log("User Name:", userName); // Output: User Name: John Doe console.log("User Age:", userAge); // Output: User Age: Age not specified console.log("User Theme:", userTheme); // Output: User Theme: dark console.log("Notifications Enabled:", notificationsEnabled); // Output: Notifications Enabled: true (default is used as notifications is null)
Here, ?? gracefully handles cases where user.age is missing and provides a sensible default. Critically, for notificationsEnabled, even though user.preferences.notifications is set to null (meaning notifications are explicitly disabled), ?? correctly provides the default true only if it were null or undefined in the first place - if the goal was to default to true when notifications preference was not set at all, but respect explicit null as disabled, then ?? would need to be used carefully depending on the desired logic. In this case, assuming null means intentionally disabled, the default true is only applied when truly unset (undefined or null).
3. Chaining with Optional Chaining (?.)
The ?? operator works particularly well with the Optional Chaining operator (?. ). Optional chaining allows you to safely access properties of deeply nested objects that might be null or undefined. Combining ?. with ?? lets you elegantly handle nested optional properties and provide defaults if any part of the chain is nullish.
javascriptconst config = { api: { endpoints: { user: "/api/users", // settings endpoint might be missing in some configurations } } }; const userEndpoint = config.api.endpoints.user ?? "/default/users"; const settingsEndpoint = config.api.endpoints.settings ?? "/default/settings"; // settings is missing, so default applies const dashboardEndpoint = config.api.endpoints.dashboard?.url ?? "/default/dashboard"; // dashboard and url are missing, optional chaining prevents error, then default applies console.log("User Endpoint:", userEndpoint); // Output: User Endpoint: /api/users console.log("Settings Endpoint:", settingsEndpoint); // Output: Settings Endpoint: /default/settings console.log("Dashboard Endpoint:", dashboardEndpoint); // Output: Dashboard Endpoint: /default/dashboard
In this configuration example, optional chaining (?. ) ensures that if config.api.endpoints.dashboard or .url is missing, it won't throw an error, and the ?? operator then provides the default endpoint.
Double Question Mark (??) vs. Logical OR (||) - Differences Explained
While both ?? and || can be used to provide default values, it’s crucial to understand their fundamental differences to use them appropriately. The key distinction lies in what they consider to be a "default-triggering" value.
Feature | Nullish Coalescing Operator (??) | Logical OR Operator (||) |
---|---|---|
Trigger Values | null, undefined | Falsy values: false, 0, -0, "", null, undefined, NaN, 0n |
Purpose | Provide default for truly missing values | Provide default for any falsy value |
Use Cases | Default when value is intentionally not set | Default when value is logically "false" |
Precision | More precise, distinguishes valid falsy values | Less precise, treats all falsy as equal |
Let’s illustrate the difference with examples:
javascriptlet valueWith_OR; let valueWith_QQ; // Example 1: Using 0 valueWith_OR = 0 || "Default Value"; valueWith_QQ = 0 ?? "Default Value"; console.log("0 with ||:", valueWith_OR); // Output: 0 with ||: Default Value (0 is falsy, so default used) console.log("0 with ??:", valueWith_QQ); // Output: 0 with ??: 0 (0 is NOT nullish, so 0 is used) // Example 2: Using empty string valueWith_OR = "" || "Default Value"; valueWith_QQ = "" ?? "Default Value"; console.log("'' with ||:", valueWith_OR); // Output: '' with ||: Default Value ("" is falsy, so default used) console.log("'' with ??:", valueWith_QQ); // Output: '' with ??: ("" is NOT nullish, so "" is used) // Example 3: Using false valueWith_OR = false || "Default Value"; valueWith_QQ = false ?? "Default Value"; console.log("false with ||:", valueWith_OR); // Output: false with ||: Default Value (false is falsy, so default used) console.log("false with ??:", valueWith_QQ); // Output: false with ??: false (false is NOT nullish, so false is used) // Example 4: Using null valueWith_OR = null || "Default Value"; valueWith_QQ = null ?? "Default Value"; console.log("null with ||:", valueWith_OR); // Output: null with ||: Default Value (null is falsy and nullish, default used) console.log("null with ??:", valueWith_QQ); // Output: null with ??: Default Value (null is nullish, default used) // Example 5: Using undefined valueWith_OR = undefined || "Default Value"; valueWith_QQ = undefined ?? "Default Value"; console.log("undefined with ||:", valueWith_OR); // Output: undefined with ||: Default Value (undefined is falsy and nullish, default used) console.log("undefined with ??:", valueWith_QQ); // Output: undefined with ??: Default Value (undefined is nullish, default used)
As you can see, || treats 0, "", and false as triggers for the default value, while ?? only uses the default for null and undefined. Choose ?? when you need to distinguish between intentionally set falsy values and truly missing values.
FAQs
- When should I use ?? instead of ||? Use ?? when you want to provide a default value specifically for null or undefined, and you need to treat other falsy values like 0 or "" as valid values, not to be replaced by defaults. Use || when you want to provide a default for any falsy value.
- Is ?? widely supported in browsers and Node.js? Yes, the Nullish Coalescing Operator (??) was introduced in ES2020 and is widely supported in modern browsers and Node.js versions (version 14.0 or later). If you are targeting older environments, you might need to consider transpilation or check compatibility.
- Is there a performance difference between ?? and ||? Similar to !! vs. Boolean(), the performance difference between ?? and || is likely negligible in most real-world scenarios. Both are very efficient operators. Choose based on semantic correctness and code clarity rather than performance.
- Can ?? be chained? Yes, ?? can be chained, similar to ||. For example, a ?? b ?? c will evaluate to a if a is not nullish, otherwise to b if b is not nullish, and finally to c if both a and b are nullish.
- Is using ?? considered good practice? Yes, using ?? is considered good practice when it improves the clarity and correctness of your code, especially in cases where you specifically need to handle nullish values differently from other falsy values. It leads to more precise and intention-revealing code compared to always using ||.
Conclusion
The Double Question Mark (??), or Nullish Coalescing Operator, is a valuable tool in JavaScript for handling default values with precision. By focusing solely on null and undefined, it allows you to distinguish between genuinely missing values and other falsy values like 0 or "". This distinction is crucial in many programming scenarios, making your code more robust and behaviorally accurate.
Understanding the difference between ?? and || is key to leveraging the right operator for the right situation. While || remains useful for general falsy-based defaults, ?? shines when you need to be specific about nullish values. As you continue to develop in JavaScript, mastering ?? will enhance your ability to write cleaner, more intent-driven, and less error-prone code. Embrace the precision of the nullish coalescing operator and elevate your JavaScript toolkit. Happy coding!
Follow and Support me on Medium and Patreon. Clap and Comment on Medium Posts if you find this helpful for you. Thanks for reading it!!!