與許多其他語言不同,JavaScript 裡有兩種不同的東西,用來代表缺少(有意義的)值。
它們就是 null 和 undefined。
原始值 null 用來表示變數刻意設定成的「空值」。
在其他語言裡,類似的結構可能只用於(缺少的)物件或指標型別。
在 JavaScript 中,null 通常代表任何型別的空值。
let name = null;
// name is intentionally set to "empty" because it is not
// available
你可以用嚴格相等運算子===檢查變數是否為 null。
雖然 null 是原始值,但出於歷史因素,typeof 運算子會「錯誤地」回傳 object。
這表示不能單靠它來檢查變數是否為 null。
let name = null;
name === null;
// => true
// Pitfall:
typeof name;
// => 'object'
尚未被指定值的變數,型別為
undefined。1
這表示 null 代表空值(但仍然是個值),而 undefined 代表完全沒有值。🤯
undefined 會出現在各種不同的情境中。
undefined。undefined。undefined。undefined,除非它有預設值。let name;
console.log(name);
// => undefined
let obj = { greeting: 'hello world' };
console.log(obj.missingKey);
// => undefined
function returnNothing() {
return;
}
console.log(returnNothing());
// => undefined
你可以用嚴格相等運算子===或typeof運算子來檢查變數是否為 undefined。
let name;
name === undefined;
// => true
typeof name === 'undefined';
// => true
不建議手動把 undefined 指定給變數,請改成一律使用 null,清楚表達你是刻意設定空值。
如前面提到的,在 JavaScript 中存取物件裡不存在的鍵會回傳 undefined。
不過,如果你試著取得巢狀的值,而父層的鍵不存在,就會對 undefined 取值,導致 TypeError: Cannot read property ... of undefined。
理論上,你總是需要先檢查父層的鍵存在,才能試著取得巢狀的鍵。
以往這通常用 AND 運算子&&來做,但對於深層巢狀的值,這會產生非常冗長的運算式。
obj.level1 && obj.level1.level2 && obj.level1.level2.level3;
為了解決這個問題,語言規格在 2020 年加入了選擇性鏈結。
有了選擇性鏈結運算子?.,你可以確保 JavaScript 只在父層不是 null 或 undefined 時,才嘗試存取巢狀的鍵。
否則就會回傳 undefined。
const obj = {
address: {
street: 'Trincomalee Highway',
city: 'Batticaloa',
},
};
obj.residence;
// => undefined
obj.address.zipCode;
// => undefined
obj.residence.street;
// => TypeError: Cannot read property 'street' of undefined
obj.residence?.street;
// => undefined
obj.residence?.street?.number;
// => undefined
有些情況下,你會想在變數為 null 或 undefined 時套用預設值。
以前這通常用三元運算子?來做,或利用 OR 運算子||的惰性求值。
這有個缺點:只要變數是 falsy(例如 '' 或 0),預設值就會被套用,而不只在它是 null 或 undefined 的時候。
這很容易造成意料之外的結果。
let amount = null;
amount = amount || 1;
// => 1
amount = 0;
amount = amount || 1;
// => 1
amount = 0;
amount ? amount : 1;
// => 1
為了處理這點,空值合併運算子??被引進。
就像選擇性鏈結一樣,它也在 2020 年被加入語言規格。
空值合併運算子??只有在左邊的運算元是 null 或 undefined 時,才會回傳右邊的運算元。
否則就回傳左邊的運算元。
這樣一來,就能更精確地套用預設值。
let amount = null;
amount = amount ?? 1;
// => 1
amount = 0;
amount = amount ?? 1;
// => 0
[1] Undefined, MDN. (2021). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined (2021 年 6 月 4 日存取)。