?. で安全に取り出す
このレッスンで分かること
obj?.propは左がnullかundefinedのとき、そこで打ち切ってundefinedを返します- 使うのは「無いことが正常」な場所だけにします
1 つ手前が空だっただけで、プログラムが止まる
オブジェクトの中にオブジェクトを入れると、途中の階層が無いという状態が起こります。
JavaScript
const author = { name: "山田 太郎" };
console.log(author.contact.email);
// TypeError: Cannot read properties of undefined (reading 'email')author.contact は存在しないので undefined です。その undefined からさらに .email を読もうとした瞬間、例外が投げられて止まります。
このエラーで読むべきなのは reading 'email' の部分ではなく、of undefined の部分です。犯人は email ではなく、その 1 つ手前の contact だからです。読もうとしたキーの名前ではなく、その手前が空だった、と読み替えるのがコツです。
?. が使えるようになる前は、階層の数だけ && を並べていました。
JavaScript
const email = author && author.contact && author.contact.email;?. は左が空なら、そこで打ち切る
同じことが 1 行で書けます。
JavaScript
const email = author?.contact?.email;
console.log(email); // undefined エラーにはならない読み方は「左が null か undefined なら、そこで打ち切って undefined を返す。そうでなければ普通にプロパティを読む」です。
| 書き方 | author が空のとき | author.contact が無いとき |
|---|---|---|
author.contact.email | 止まる | 止まる |
author && author.contact && author.contact.email | undefined | undefined |
author?.contact?.email | undefined | undefined |
?. が反応するのは null と undefined の 2 つだけです。0 や "" や false は「ある」と扱われて先へ進みます。判定の基準が ?? とまったく同じなので、この 2 つは組で使えます。
JavaScript
const shown = author?.contact?.email ?? "未公開";?. の結果は undefined になることがあり、そのまま画面に出すと「undefined」という文字が表示されてしまいます。既定値を添えるのが定石です。
どこに使い、どこに使わないか
便利だからといって、すべての . を ?. に置き換えるのは行き過ぎです。何を渡しても静かに undefined が返るようになり、本来ならエラーで気付けたはずの不具合が、画面に「undefined」と出るまで表面化しなくなります。
?. を使うのは、無いことが正常な値に限ります。まだ公開していない作品に URL が無いのは当たり前なので ?. が向いています。一方で題名は必ずあるべき値なので、無いなら気付けたほうが良いのです。外から来たデータ、ユーザーの入力、設定ファイルのように、自分で作っていない値の入り口が主な出番です。
よくある間違い
?.を置く位置が 1 つ後ろ —author.contact?.emailでは、author自体がundefinedのときに結局止まります。守るのは読む対象ではなく、その手前です- 代入の左側で使おうとする —
?.は読み取り専用の記号です。author?.contact.email = "..."は構文エラーになります
要件
- 関数名は
getWorkUrl、引数はworkの 1 つ work.links.urlの値を返すことworkがnullのときもwork.linksが無いときもエラーにしないこと- 取り出せないときの戻り値は
undefinedにすること
入出力例
getWorkUrl({"links":{"url":"https://example.com/profile"},"title":"プロフィールサイト"}) → "https://example.com/profile"
getWorkUrl({"links":{},"title":"自己紹介カード"}) → null
getWorkUrl({"title":"スキル一覧の表"}) → null
getWorkUrl(null) → null
getWorkUrl({"links":{"url":"https://example.com/card"},"title":"自己紹介カード"}) → "https://example.com/card"