JavaScript Nullish Coalescing (空值合併運算子)

Nullish Coalescing(空值合併運算子)是 ES2020 引入的語法,使用 ?? 運算子。它用來在變數值為 nullundefined 時提供預設值。

基本語法

const result = value ?? defaultValue;

valuenullundefined 時,回傳 defaultValue;否則回傳 value

const a = null ?? 'default';
console.log(a); // 'default'

const b = undefined ?? 'default';
console.log(b); // 'default'

const c = 'hello' ?? 'default';
console.log(c); // 'hello'

?? 與 || 的差異

傳統上我們會用 || 來提供預設值:

const name = userName || 'Guest';

|| 會在左邊的值是任何 falsy 值時使用右邊的預設值。JavaScript 的 falsy 值包括:false0''(空字串)、nullundefinedNaN

這可能導致非預期的結果:

// 使用 ||
const count = 0 || 10; // 10(0 是 falsy)
const text = '' || 'default'; // 'default'(空字串是 falsy)
const flag = false || true; // true(false 是 falsy)

// 使用 ??
const count2 = 0 ?? 10; // 0(0 不是 null/undefined)
const text2 = '' ?? 'default'; // ''(空字串不是 null/undefined)
const flag2 = false ?? true; // false(false 不是 null/undefined)
?? 只在值是 nullundefined 時才會使用預設值,其他 falsy 值(如 0''false)會被保留。

實用範例

設定物件屬性預設值

function createUser(options) {
  return {
    name: options.name ?? 'Anonymous',
    age: options.age ?? 0,
    isAdmin: options.isAdmin ?? false,
    bio: options.bio ?? '',
  };
}

const user = createUser({ name: 'Mike' });
// { name: 'Mike', age: 0, isAdmin: false, bio: '' }

// 如果用 ||,age、isAdmin、bio 都會被覆蓋
function createUserWrong(options) {
  return {
    name: options.name || 'Anonymous',
    age: options.age || 18, // 如果傳入 0,會變成 18
    isAdmin: options.isAdmin || false,
    bio: options.bio || 'No bio', // 如果傳入 '',會變成 'No bio'
  };
}

處理函式參數

function greet(name, greeting) {
  name = name ?? 'Guest';
  greeting = greeting ?? 'Hello';

  console.log(greeting + ', ' + name + '!');
}

greet(); // 'Hello, Guest!'
greet('Mike'); // 'Hello, Mike!'
greet('Mike', 'Hi'); // 'Hi, Mike!'
greet('Mike', ''); // ', Mike!'(空字串被保留)

存取可能不存在的資料

const user = {
  settings: {
    theme: null,
    fontSize: 0,
    showNotifications: false,
  },
};

// 使用 ?? 保留有效的 falsy 值
const theme = user.settings.theme ?? 'light'; // 'light'
const fontSize = user.settings.fontSize ?? 16; // 0(保留)
const showNotif = user.settings.showNotifications ?? true; // false(保留)

搭配 Optional Chaining

?? 經常和 Optional Chaining (?.) 一起使用:

const user = {
  profile: {
    name: 'Mike',
  },
};

// 安全存取並提供預設值
const city = user?.profile?.address?.city ?? 'Unknown';
console.log(city); // 'Unknown'

const name = user?.profile?.name ?? 'Guest';
console.log(name); // 'Mike'

短路求值

?? 運算子也有短路求值的特性。如果左邊不是 nullundefined,右邊的運算式不會被執行:

let count = 0;

const value = 'hello' ?? count++; // count++ 不會執行
console.log(count); // 0

const value2 = null ?? count++; // count++ 會執行
console.log(count); // 1

不能直接與 && 或 || 混用

?? 不能直接和 &&|| 混合使用,必須用括號明確指定優先順序:

// 錯誤:會拋出 SyntaxError
var result = a || b ?? c;
var result = a ?? b && c;

// 正確:使用括號
var result = (a || b) ?? c;
var result = a ?? (b && c);

這個限制是為了避免混淆,因為這三個運算子的行為不同。

Nullish Coalescing Assignment (??=)

ES2021 引入了 ??= 運算子,可以更簡潔地賦值:

const obj = { a: null, b: 'hello' };

// 只有當 a 是 null 或 undefined 時才賦值
obj.a ??= 'default';
console.log(obj.a); // 'default'

// b 不是 null/undefined,不會被改變
obj.b ??= 'new value';
console.log(obj.b); // 'hello'

// 等同於
obj.a = obj.a ?? 'default';

實際應用場景

設定 API 參數

async function fetchUsers(options) {
  const page = options?.page ?? 1;
  const limit = options?.limit ?? 10;
  const sortBy = options?.sortBy ?? 'createdAt';

  const url = '/api/users?page=' + page + '&limit=' + limit + '&sort=' + sortBy;
  return fetch(url);
}

// 使用預設值
fetchUsers({}); // page=1, limit=10
fetchUsers({ page: 2 }); // page=2, limit=10
fetchUsers({ limit: 0 }); // limit=0(0 被保留)

表單處理

function processForm(formData) {
  return {
    email: formData.email ?? '',
    age: formData.age ?? null,
    subscribed: formData.subscribed ?? false,
    // 空字串和 0 會被保留,只有 null/undefined 才使用預設值
  };
}

瀏覽器支援

Nullish Coalescing 是 ES2020 的功能,現代瀏覽器都已支援:

瀏覽器支援版本
Chrome80+
Firefox72+
Safari13.1+
Edge80+
Node.js14+

總結

運算子使用預設值的條件
||左邊是 falsy 值(false0''nullundefinedNaN
??左邊是 nullundefined

選擇建議:

  • 如果 0''false 是有效值,應使用 ??
  • 如果想把所有 falsy 值都視為「沒有值」,使用 ||