?? で既定値を用意する
このレッスンで分かること
A ?? BはAがnullかundefinedのときだけBを返します||は falsy すべてに反応するので、0や空文字まで既定値に置き換えてしまいます
閲覧数 0 の作品が「未計測」と表示される
値が未設定のときに代わりの値を出す処理は、アプリを作るとほぼ必ず登場します。前のレッスンの || を使えば、左が空っぽのときに右へ倒せます。ところが、この書き方には落とし穴があります。
JavaScript
const views = 0; // 計測はできていて、結果が 0 だった
console.log("閲覧数 " + (views || "未計測")); // 閲覧数 未計測計測できていないわけではないのに、「未計測」と表示されてしまいました。0 は falsy なので、|| が右辺に切り替えたからです。|| は「空っぽに見えるもの」すべてに反応する、というのが原因です。
?? が反応するのは null と undefined だけ
?? は null 合体演算子と呼び、左辺が null か undefined のときだけ右辺を返します。0 や空文字はそのまま残ります。
| 左辺の値 | 左辺 || "既定値" | 左辺 ?? "既定値" |
|---|---|---|
"人気" | "人気" | "人気" |
null | "既定値" | "既定値" |
undefined | "既定値" | "既定値" |
0 | "既定値" | 0 |
"" | "既定値" | "" |
false | "既定値" | false |
下の 3 行が要点です。0 "" false は「値が入っていない」のではなく「その値が入っている」状態です。?? はそれを尊重して、そのまま返します。
JavaScript
console.log(0 || "未計測"); // 未計測 0 が消える
console.log(0 ?? "未計測"); // 0 0 が残る
console.log("" || "未入力"); // 未入力
console.log("" ?? "未入力"); // 空文字がそのまま返る使い分けの目安は次のとおりです。「値が存在しないときだけ埋めたい」なら
??、「空欄もまとめて埋めたい」なら||です。空文字を未入力とみなしたい欄なら||のほうが正しい、という場面もあります。どちらが正しいかは仕様が決めます。
つなげると候補の優先順位になる
?? は連続して書けます。左から順に見ていき、最初に null でも undefined でもない値が採用されます。外から来たデータを画面に出すときに効いてきます。API の返す JSON、フォームの入力値、保存された設定。どれも「その項目が無い」ことがふつうに起こります。
JavaScript
const saved = { theme: null, fontSize: 0 };
const preset = { theme: "dark" };
console.log(saved.theme ?? preset.theme ?? "light"); // dark 左から順に採用
console.log(saved.fontSize ?? 16); // 0 そのまま残る
console.log(saved.language ?? "ja"); // ja 項目が無いので既定値fontSize に注目してください。0 という設定値が入っているので、?? はそれを尊重します。|| を使っていたら 16 に書き換わり、ユーザーがわざわざ 0 に設定した意味が消えます。ユーザーが選んだ値を勝手に上書きしない、という点で ?? は誠実な演算子です。
なお、渡されなかった引数は undefined になります。?? は undefined にも反応するので、引数を渡し忘れた呼び出しでも既定値へ倒れて壊れません。
よくある間違い
0を消してしまう —count || 10は、countが0のとき10になります。件数や金額のように0に意味がある値には??を使ってください??を||や&&と括弧なしで混ぜる —a ?? b || cは SyntaxError です。どちらを先に評価するかが紛らわしいため、言語仕様として括弧を必須にしています。(a ?? b) || cのように、自分で書いて明示してください
JavaScript
// const theme = saved.theme ?? preset.theme || "light"; // SyntaxError
const theme = (saved.theme ?? preset.theme) || "light"; // 括弧を書けば動く要件
- 関数名は
displayName、引数はnicknameとfullNameの 2 つにすること ??を使い、nullとundefinedのときだけ次の候補に進むこと- 空文字
""はそのまま返すこと (||を使うと空文字が消えてしまう) - どちらも未設定なら文字列
"名無し"を返すこと
入出力例
displayName("たろう", "山田 太郎") → "たろう"
displayName(null, "山田 太郎") → "山田 太郎"
displayName("", "山田 太郎") → ""
displayName(null, null) → "名無し"
displayName() → "名無し"