javascript
exercises
exercises.js⚡javascript
/**
* ============================================
* 3.9 NULLISH COALESCING - EXERCISES
* ============================================
*/
// ============================================
// EXERCISE 1: Basic Nullish Coalescing
// ============================================
/**
* Task: Use ?? to provide defaults for these values
* - For null value, default to "Not available"
* - For undefined value, default to "Unknown"
* - For 0 value, keep the 0
* - For empty string, keep the empty string
*/
const nullValue = null;
const undefinedValue = undefined;
const zeroValue = 0;
const emptyString = '';
// Your code here:
// const result1 = nullValue ?? ...
// const result2 = undefinedValue ?? ...
// const result3 = zeroValue ?? ...
// const result4 = emptyString ?? ...
// console.log("Exercise 1:");
// console.log("null:", result1); // "Not available"
// console.log("undefined:", result2); // "Unknown"
// console.log("0:", result3); // 0
// console.log("'':", result4); // ""
// ============================================
// EXERCISE 2: ?? vs || Comparison
// ============================================
/**
* Task: For the value 0, show the difference between ?? and ||
*/
const count = 0;
// Your code here:
// const withOr = count || ...
// const withNullish = count ?? ...
// console.log("Exercise 2:");
// console.log("Using ||:", withOr); // "No items"
// console.log("Using ??:", withNullish); // 0
// ============================================
// EXERCISE 3: Configuration Object
// ============================================
/**
* Task: Create a function that merges user config with defaults
* using ?? to preserve 0, false, and empty string values
*
* Defaults: { theme: 'light', volume: 50, muted: false }
*/
// Your code here:
// function mergeConfig(userConfig) { }
const userConfig = { theme: 'dark', volume: 0, muted: true };
// console.log("Exercise 3:");
// console.log(mergeConfig(userConfig));
// // { theme: 'dark', volume: 0, muted: true }
// console.log(mergeConfig({}));
// // { theme: 'light', volume: 50, muted: false }
// ============================================
// EXERCISE 4: Chained Nullish Coalescing
// ============================================
/**
* Task: Given these variables, use chained ?? to find
* the first non-nullish value
*/
const primary = null;
const secondary = undefined;
const tertiary = 0;
const fallback = 'default';
// Your code here:
// const result = ...
// console.log("Exercise 4:", result); // 0
// ============================================
// EXERCISE 5: Nullish Assignment
// ============================================
/**
* Task: Use ??= to set default values only where needed
*/
const settings = {
color: null,
size: 0,
enabled: false,
label: undefined,
};
// Your code here - use ??= to set defaults:
// settings.color ??= ...
// settings.size ??= ...
// settings.enabled ??= ...
// settings.label ??= ...
// console.log("Exercise 5:", settings);
// // { color: 'red', size: 0, enabled: false, label: 'Default' }
// ============================================
// EXERCISE 6: Form Input Processing
// ============================================
/**
* Task: Process form input where empty strings and 0 are valid
* Only null/undefined should get defaults
*/
function processInput(input) {
// Your code here:
// return {
// name: ...,
// age: ...,
// bio: ...,
// notifications: ...
// };
}
// console.log("Exercise 6:");
// console.log(processInput({ name: '', age: 0, bio: null, notifications: false }));
// // { name: '', age: 0, bio: 'No bio', notifications: false }
// ============================================
// EXERCISE 7: With Optional Chaining
// ============================================
/**
* Task: Safely access nested properties and provide defaults
*/
const user = {
profile: {
settings: {
theme: 'dark',
},
},
};
// Your code here - use ?. and ??:
// const theme = ...
// const fontSize = ...
// const volume = ...
// console.log("Exercise 7:");
// console.log("theme:", theme); // 'dark'
// console.log("fontSize:", fontSize); // 14 (default)
// console.log("volume:", volume); // 50 (default)
// ============================================
// EXERCISE 8: API Response Handler
// ============================================
/**
* Task: Handle API response data with proper defaults
* Some fields might be null, undefined, 0, or empty string
*/
function handleResponse(response) {
// Your code here:
// const userName = ...
// const score = ...
// const message = ...
// const isActive = ...
// return { userName, score, message, isActive };
}
const apiResponse = {
user: { name: 'John', score: 0 },
message: '',
isActive: false,
};
// console.log("Exercise 8:");
// console.log(handleResponse(apiResponse));
// // { userName: 'John', score: 0, message: '', isActive: false }
// console.log(handleResponse({}));
// // { userName: 'Guest', score: 0, message: 'Welcome!', isActive: true }
// ============================================
// EXERCISE 9: Fix the Bug
// ============================================
/**
* Task: This code has a bug - it uses || when it should use ??
* Fix it so that 0 and false are preserved
*/
function getDisplayValue(value, defaultValue) {
// Bug: Using || instead of ??
return value || defaultValue; // Fix this line
}
// console.log("Exercise 9:");
// console.log(getDisplayValue(0, 100)); // Should be 0, not 100
// console.log(getDisplayValue(false, true)); // Should be false, not true
// console.log(getDisplayValue(null, 'N/A')); // Should be 'N/A'
// ============================================
// EXERCISE 10: Lazy Initialization
// ============================================
/**
* Task: Create a cache that lazily initializes values using ??=
*/
const cache = {};
function getOrCompute(key, computeFn) {
// Your code here - use ??=
// cache[key] ??= ...
// return cache[key];
}
// const expensiveCompute = () => {
// console.log("Computing...");
// return Math.random();
// };
// console.log("Exercise 10:");
// console.log("First call:", getOrCompute('random', expensiveCompute));
// console.log("Second call:", getOrCompute('random', expensiveCompute));
// // Should only log "Computing..." once
// ============================================
// EXERCISE 11: Parentheses Required
// ============================================
/**
* Task: Correctly combine ?? with || using parentheses
*
* Get the first truthy value from a and b,
* or use 'fallback' if both are nullish
*/
const a = 0; // falsy but not nullish
const b = false; // falsy but not nullish
const c = null; // nullish
// Your code here - needs parentheses:
// const result = (a || b) ?? 'fallback';
// console.log("Exercise 11:", result); // false (b is truthy compared to 0)
// ============================================
// EXERCISE 12: Complete Function
// ============================================
/**
* Task: Create a function that handles all edge cases
* for rendering a user profile
*/
function renderProfile(user) {
// Your code here - handle all cases:
// - name could be null/undefined/empty string
// - age could be null/undefined/0
// - bio could be null/undefined/empty string
// - isVerified could be null/undefined/false
// return {
// displayName: ...,
// displayAge: ...,
// displayBio: ...,
// verifiedBadge: ...
// };
}
// console.log("Exercise 12:");
// console.log(renderProfile({ name: '', age: 0, bio: '', isVerified: false }));
// // { displayName: '', displayAge: 0, displayBio: '', verifiedBadge: false }
// console.log(renderProfile({}));
// // { displayName: 'Anonymous', displayAge: 'N/A', displayBio: 'No bio', verifiedBadge: false }
// ============================================
// SOLUTIONS
// ============================================
/**
* Exercise 1:
* const result1 = nullValue ?? "Not available";
* const result2 = undefinedValue ?? "Unknown";
* const result3 = zeroValue ?? "Default";
* const result4 = emptyString ?? "Empty";
*
* Exercise 2:
* const withOr = count || "No items";
* const withNullish = count ?? "No items";
*
* Exercise 3:
* function mergeConfig(userConfig) {
* return {
* theme: userConfig.theme ?? 'light',
* volume: userConfig.volume ?? 50,
* muted: userConfig.muted ?? false
* };
* }
*
* Exercise 4:
* const result = primary ?? secondary ?? tertiary ?? fallback;
*
* Exercise 5:
* settings.color ??= 'red';
* settings.size ??= 100;
* settings.enabled ??= true;
* settings.label ??= 'Default';
*
* Exercise 6:
* function processInput(input) {
* return {
* name: input.name ?? 'Guest',
* age: input.age ?? 0,
* bio: input.bio ?? 'No bio',
* notifications: input.notifications ?? true
* };
* }
*
* Exercise 7:
* const theme = user?.profile?.settings?.theme ?? 'light';
* const fontSize = user?.profile?.settings?.fontSize ?? 14;
* const volume = user?.profile?.settings?.volume ?? 50;
*
* Exercise 8:
* function handleResponse(response) {
* const userName = response?.user?.name ?? 'Guest';
* const score = response?.user?.score ?? 0;
* const message = response?.message ?? 'Welcome!';
* const isActive = response?.isActive ?? true;
* return { userName, score, message, isActive };
* }
*
* Exercise 9:
* function getDisplayValue(value, defaultValue) {
* return value ?? defaultValue;
* }
*
* Exercise 10:
* function getOrCompute(key, computeFn) {
* cache[key] ??= computeFn();
* return cache[key];
* }
*
* Exercise 11:
* const result = (a || b) ?? 'fallback'; // false
*
* Exercise 12:
* function renderProfile(user) {
* return {
* displayName: user.name ?? 'Anonymous',
* displayAge: user.age ?? 'N/A',
* displayBio: user.bio ?? 'No bio',
* verifiedBadge: user.isVerified ?? false
* };
* }
*/