Logical assignment operators combine logical operations with assignment for cleaner code. Here's how to use them.
Nullish Coalescing Assignment (??=)#
1// Assign if null or undefined
2let x = null;
3x ??= 'default';
4console.log(x); // 'default'
5
6let y = undefined;
7y ??= 'default';
8console.log(y); // 'default'
9
10// Does NOT assign for falsy values
11let z = 0;
12z ??= 100;
13console.log(z); // 0 (not reassigned)
14
15let str = '';
16str ??= 'fallback';
17console.log(str); // '' (not reassigned)
18
19let bool = false;
20bool ??= true;
21console.log(bool); // false (not reassigned)
22
23// Equivalent to
24// x = x ?? 'default';
25// if (x === null || x === undefined) x = 'default';Logical OR Assignment (||=)#
1// Assign if falsy
2let x = 0;
3x ||= 100;
4console.log(x); // 100 (0 is falsy)
5
6let str = '';
7str ||= 'default';
8console.log(str); // 'default' ('' is falsy)
9
10let bool = false;
11bool ||= true;
12console.log(bool); // true (false is falsy)
13
14let y = null;
15y ||= 'fallback';
16console.log(y); // 'fallback'
17
18// Doesn't assign for truthy values
19let z = 'hello';
20z ||= 'default';
21console.log(z); // 'hello' (not reassigned)
22
23// Equivalent to
24// x = x || 100;
25// if (!x) x = 100;Logical AND Assignment (&&=)#
1// Assign if truthy
2let x = 'hello';
3x &&= 'world';
4console.log(x); // 'world'
5
6let y = 100;
7y &&= 200;
8console.log(y); // 200
9
10// Doesn't assign for falsy values
11let z = null;
12z &&= 'value';
13console.log(z); // null (not reassigned)
14
15let num = 0;
16num &&= 100;
17console.log(num); // 0 (not reassigned)
18
19// Equivalent to
20// x = x && 'world';
21// if (x) x = 'world';Object Property Defaults#
1// Initialize missing properties
2const config = {
3 theme: 'dark',
4 // debug is missing
5};
6
7config.debug ??= false;
8config.timeout ??= 5000;
9config.theme ??= 'light'; // Won't override
10
11console.log(config);
12// { theme: 'dark', debug: false, timeout: 5000 }
13
14// Nested properties
15const user = {
16 settings: {},
17};
18
19user.settings.notifications ??= true;
20user.settings.language ??= 'en';
21
22// Compare with ||= for strings that might be empty
23const form = {
24 name: '',
25 email: 'user@example.com',
26};
27
28form.name ||= 'Anonymous'; // Assigns (empty string is falsy)
29form.email ||= 'default@test'; // Doesn't assign
30
31form.name ??= 'Anonymous'; // Wouldn't assign ('' is not null)Function Parameters#
1// Default parameter values
2function connect(options) {
3 options.host ??= 'localhost';
4 options.port ??= 3000;
5 options.ssl ??= false;
6
7 return options;
8}
9
10connect({ host: 'api.example.com' });
11// { host: 'api.example.com', port: 3000, ssl: false }
12
13// Mutable defaults
14function processData(data, options = {}) {
15 options.format ??= 'json';
16 options.validate ??= true;
17 options.transform ??= (x) => x;
18
19 return options.transform(data);
20}
21
22// Cache initialization
23function getOrCompute(cache, key, compute) {
24 cache[key] ??= compute();
25 return cache[key];
26}
27
28const cache = {};
29getOrCompute(cache, 'result', () => expensiveCalculation());Lazy Initialization#
1// Initialize on first access
2class LazyLoader {
3 constructor() {
4 this._data = null;
5 }
6
7 get data() {
8 return (this._data ??= this.loadData());
9 }
10
11 loadData() {
12 console.log('Loading data...');
13 return { items: [] };
14 }
15}
16
17const loader = new LazyLoader();
18console.log(loader.data); // Loads on first access
19console.log(loader.data); // Uses cached value
20
21// Singleton pattern
22class Database {
23 static instance;
24
25 static getInstance() {
26 return (Database.instance ??= new Database());
27 }
28}
29
30// Memoization
31function memoize(fn) {
32 const cache = {};
33
34 return function (...args) {
35 const key = JSON.stringify(args);
36 return (cache[key] ??= fn.apply(this, args));
37 };
38}Array Operations#
1// Initialize array if needed
2const groups = {};
3
4function addToGroup(groupName, item) {
5 groups[groupName] ??= [];
6 groups[groupName].push(item);
7}
8
9addToGroup('users', 'Alice');
10addToGroup('users', 'Bob');
11addToGroup('admins', 'Charlie');
12
13// { users: ['Alice', 'Bob'], admins: ['Charlie'] }
14
15// Counting occurrences
16const counts = {};
17
18function count(item) {
19 counts[item] ??= 0;
20 counts[item]++;
21}
22
23['a', 'b', 'a', 'c', 'b', 'a'].forEach(count);
24// { a: 3, b: 2, c: 1 }
25
26// Better with ||= for numbers starting at 0
27const counts2 = {};
28['a', 'b', 'a'].forEach((item) => {
29 counts2[item] ||= 0; // Doesn't work! 0 is falsy
30 counts2[item] ??= 0; // Works correctly
31 counts2[item]++;
32});State Management#
1// Redux-like reducer
2function reducer(state, action) {
3 switch (action.type) {
4 case 'SET_USER':
5 return {
6 ...state,
7 user: action.payload,
8 };
9
10 case 'INIT_SETTINGS':
11 // Only set if not already initialized
12 state.settings ??= {};
13 state.settings.theme ??= 'light';
14 state.settings.language ??= 'en';
15 return { ...state };
16
17 default:
18 return state;
19 }
20}
21
22// React state
23function useSettings(initial) {
24 const [settings, setSettings] = useState(() => {
25 const saved = localStorage.getItem('settings');
26 const parsed = saved ? JSON.parse(saved) : {};
27
28 parsed.theme ??= initial.theme ?? 'light';
29 parsed.fontSize ??= initial.fontSize ?? 16;
30
31 return parsed;
32 });
33
34 return [settings, setSettings];
35}DOM Manipulation#
1// Element data storage
2function setElementData(element, key, value) {
3 element._customData ??= {};
4 element._customData[key] = value;
5}
6
7function getElementData(element, key, defaultValue) {
8 element._customData ??= {};
9 element._customData[key] ??= defaultValue;
10 return element._customData[key];
11}
12
13// Event handler registry
14const handlers = new WeakMap();
15
16function addHandler(element, type, handler) {
17 let elementHandlers = handlers.get(element);
18 elementHandlers ??= {};
19 elementHandlers[type] ??= [];
20 elementHandlers[type].push(handler);
21 handlers.set(element, elementHandlers);
22}API Response Handling#
1// Normalize API response
2function normalizeUser(apiUser) {
3 const user = { ...apiUser };
4
5 user.displayName ??= user.name ?? 'Anonymous';
6 user.avatar ??= '/default-avatar.png';
7 user.role ??= 'user';
8 user.preferences ??= {};
9 user.preferences.notifications ??= true;
10
11 return user;
12}
13
14// Default values for missing fields
15async function fetchUserData(userId) {
16 const response = await fetch(`/api/users/${userId}`);
17 const data = await response.json();
18
19 data.createdAt ??= new Date().toISOString();
20 data.updatedAt ??= data.createdAt;
21 data.status ??= 'active';
22
23 return data;
24}Comparison Table#
1// Summary of behaviors
2const value = null;
3
4// ??= only assigns for null/undefined
5value ??= 'default'; // Assigns
6
7// ||= assigns for any falsy value
8value ||= 'default'; // Assigns
9
10// &&= assigns for any truthy value
11value &&= 'other'; // Does NOT assign (null is falsy)
12
13// With 0
14let num = 0;
15num ??= 100; // num stays 0
16num ||= 100; // num becomes 100
17num &&= 100; // num stays 0
18
19// With ''
20let str = '';
21str ??= 'x'; // str stays ''
22str ||= 'x'; // str becomes 'x'
23str &&= 'x'; // str stays ''
24
25// With false
26let bool = false;
27bool ??= true; // bool stays false
28bool ||= true; // bool becomes true
29bool &&= true; // bool stays falseBest Practices#
Usage:
✓ ??= for null/undefined defaults
✓ ||= for falsy defaults
✓ &&= for conditional updates
✓ Initialize object properties
Patterns:
✓ Lazy initialization
✓ Cache population
✓ Default parameters
✓ Object property defaults
Prefer ??= when:
✓ 0 is a valid value
✓ '' is a valid value
✓ false is a valid value
✓ Only null/undefined should trigger
Avoid:
✗ Complex chaining
✗ Side effects in right-hand side
✗ Confusion with similar operators
✗ Overusing when simple = works
Conclusion#
Logical assignment operators provide concise syntax for conditional assignments. Use ??= when only null or undefined should trigger assignment, ||= when any falsy value should trigger, and &&= when you want to replace truthy values. They're particularly useful for setting defaults, lazy initialization, and normalizing data.