InterviewHubPRO
Section Overview
Q489

Question 489 of 521

Part 8: Modern JavaScript APIs, WeakRef, Proxy & Performance (464 - 521)

94%
JavaScript Internals (521 Qs)•Part 8: Modern JavaScript APIs, WeakRef, Proxy & Performance (464 - 521)

What are logical assignment operators?

Logical assignment operators (introduced in ES2021) combine logical operations (&&, ||, ??) with assignment (=). They provide a concise way to assign values based on logical conditions.

The Three Operators:

  1. &&= - Logical AND assignment
  2. ||= - Logical OR assignment
  3. ??= - Nullish coalescing assignment

Logical AND Assignment (&&=):

Assigns the right-hand value only if the left-hand value is truthy.

javascript
 // Syntax: x &&= y
 // Equivalent to: x && (x = y)
 // or: if (x) { x = y; }

 let user = { name: 'Alice', admin: true };

 // Traditional approach
 if (user.admin) {
   user.admin = 'super';
 }

 // With &&=
 user.admin &&= 'super';
 console.log(user.admin); // 'super'

 let guest = { name: 'Bob', admin: false };
 guest.admin &&= 'super';
 console.log(guest.admin); // false (unchanged, because falsy)

 // Practical example: Conditional transformation
 const data = {
   username: 'john_doe',
   email: 'JOHN@EXAMPLE.COM'
 };

 // Normalize email only if it exists
 data.email &&= data.email.toLowerCase();
 console.log(data.email); // 'john@example.com'

 // Use case: Applying transformations
 const product = { name: 'Widget', price: 29.99 };
 product.price &&= product.price * 1.1; // Apply 10% increase
 console.log(product.price); // 32.989

Logical OR Assignment (||=):

Assigns the right-hand value only if the left-hand value is falsy.

javascript
 // Syntax: x ||= y
 // Equivalent to: x || (x = y)
 // or: if (!x) { x = y; }

 let config = { timeout: 0 };

 // Traditional approach
 if (!config.timeout) {
   config.timeout = 3000;
 }

 // With ||=
 config.timeout ||= 3000;
 console.log(config.timeout); // 3000

 // Setting default values
 let options = {};
 options.theme ||= 'dark';
 options.lang ||= 'en';
 options.debug ||= false;

 console.log(options); // { theme: 'dark', lang: 'en', debug: false }

 // Practical example: Form defaults
 function processForm(formData) {
   formData.country ||= 'USA';
   formData.newsletter ||= false;
   formData.age ||= 18;
   return formData;
 }

 console.log(processForm({ name: 'Alice' }));
 // { name: 'Alice', country: 'USA', newsletter: false, age: 18 }

 // Use case: Lazy initialization
 class Calculator {
   #cache;

   compute(x) {
     this.#cache ||= new Map(); // Initialize only once
     
     if (!this.#cache.has(x)) {
       this.#cache.set(x, x * x);
     }
     return this.#cache.get(x);
   }
 }

Nullish Coalescing Assignment (??=):

Assigns the right-hand value only if the left-hand value is null or undefined (nullish).

javascript
 // Syntax: x ??= y
 // Equivalent to: x ?? (x = y)
 // or: if (x === null || x === undefined) { x = y; }

 let settings = { volume: 0, brightness: null };

 // Traditional approach
 if (settings.brightness === null || settings.brightness === undefined) {
   settings.brightness = 50;
 }

 // With ??=
 settings.volume ??= 50;      // Unchanged (0 is not nullish)
 settings.brightness ??= 50;  // Changed (null is nullish)

 console.log(settings); // { volume: 0, brightness: 50 }

 // Key difference from ||=
 let data = {
   count: 0,
   active: false,
   name: ''
 };

 // With ||= (treats falsy values as missing)
 let copy1 = { ...data };
 copy1.count ||= 10;    // Changes to 10 (0 is falsy)
 copy1.active ||= true; // Changes to true (false is falsy)
 copy1.name ||= 'Unknown'; // Changes to 'Unknown' ('' is falsy)

 // With ??= (only treats null/undefined as missing)
 let copy2 = { ...data };
 copy2.count ??= 10;    // Stays 0 (not nullish)
 copy2.active ??= true; // Stays false (not nullish)
 copy2.name ??= 'Unknown'; // Stays '' (not nullish)

 console.log(copy1); // { count: 10, active: true, name: 'Unknown' }
 console.log(copy2); // { count: 0, active: false, name: '' }

 // Practical example: API defaults
 function fetchUser(userId, options = {}) {
   options.cache ??= true;
   options.timeout ??= 5000;
   options.retries ??= 3;
   // Note: Won't override if explicitly set to 0 or false
   
   console.log('Fetching with options:', options);
 }

 fetchUser(1, { cache: false }); 
 // { cache: false, timeout: 5000, retries: 3 }
 // cache stays false (not nullish)

Comparison Table:

javascript
 let obj = { a: 0, b: false, c: '', d: null, e: undefined };

 // &&= (assigns if truthy)
 obj.a &&= 100; // Unchanged (0 is falsy)
 obj.b &&= 100; // Unchanged (false is falsy)
 obj.c &&= 100; // Unchanged ('' is falsy)

 // ||= (assigns if falsy)
 obj.a ||= 100; // Changes to 100
 obj.b ||= 100; // Changes to 100
 obj.c ||= 100; // Changes to 100

 // ??= (assigns if nullish)
 obj.a ??= 100; // Unchanged (0 is not nullish)
 obj.b ??= 100; // Unchanged (false is not nullish)
 obj.c ??= 100; // Unchanged ('' is not nullish)
 obj.d ??= 100; // Changes to 100 (null is nullish)
 obj.e ??= 100; // Changes to 100 (undefined is nullish)

Real-World Examples:

javascript
 // 1. Component state management
 class Component {
   state = {};

   setState(newState) {
     // Merge with defaults
     newState.loading ??= false;
     newState.error ??= null;
     newState.data ??= [];
     this.state = { ...this.state, ...newState };
   }
 }

 // 2. Configuration merging
 function createConfig(userConfig) {
   const config = { ...userConfig };
   config.env ??= 'production';
   config.debug ??= false;
   config.port ??= 3000;
   config.host ??= 'localhost';
   return config;
 }

 // 3. Memoization
 const memoize = (fn) => {
   const cache = new Map();
   return (arg) => {
     cache.has(arg) ||= cache.set(arg, fn(arg));
     return cache.get(arg);
   };
 };

 // 4. Safe property updates
 function updateUser(user, updates) {
   user.lastModified &&= new Date(); // Only if already has lastModified
   user.email ??= updates.email;      // Only if email is missing
   user.role ||= 'user';              // Only if role is falsy
   return user;
 }

Benefits:

  1. Concise: Shorter than traditional if statements
  2. Readable: Clear intent - "assign if condition"
  3. Safe: Avoids unnecessary assignments and side effects
  4. Performance: Only evaluates right-hand side when needed