nullとundefinedの違いと対策をきちんと理解する

公開日:
目次

nullとundefinedの違いをきちんと理解するために調べました。

初心者でも「なんとなく」ではなく正確に理解しておかないと、いざ問題が起きたときに適切な判断ができません。また、nullやundefinedの扱いを誤ると、プログラムがクラッシュしてサービスが停止するリスクがあります。本記事では、違いを正確に理解した上で、具体的な対処法まで紹介します。

例で理解するnullとundefinedの違い

宅配便を例に考えてみます。APIからデータを取得する場面と照らし合わせながら読んでみてください。

undefined(未定義)は「宅配便がまだ届いていない」状態

荷物を注文したけど、まだ届いていない状態です。荷物があるかないか以前に、そもそも手元に届いていません。

null(空)は「宅配便は届いたけど、箱の中身が空だった」状態

荷物は届きました。でも開けてみたら何も入っていませんでした。「空であること」が確定しています。

この違いが分かると、エラーメッセージの意味も理解しやすくなります。

JavaScriptでの具体例

コードで見てみます。

// undefined: 変数を宣言したけど、値を入れていない
let myPackage;
console.log(myPackage);  // undefined

// null: 意図的に「空」を設定した
let emptyBox = null;
console.log(emptyBox);   // null

undefinedは「まだ何も設定していない」状態で、nullは「意図的に空にした」状態です。

エラーが起きる仕組み

よくあるエラーを見てみます。

let user;  // undefinedの状態

// ユーザーの名前を取得しようとする
console.log(user.name);  // エラー!

このコードを実行すると以下のエラーが出ます。

TypeError: Cannot read properties of undefined (reading 'name')

このエラーは「undefinedから name というプロパティを読もうとした」という意味です。宅配便の例で言えば「届いていない荷物の中身を確認しようとした」ようなものです。

なぜチェックが必要なのか

APIからデータを取得する場合を考えます。

// APIからユーザー情報を取得
const response = await fetch('/api/user/123');
const user = await response.json();

// ユーザーの住所を表示
console.log(user.address.city);  // ユーザーが住所を登録していなかったら?

ユーザーが住所を登録していない場合、 user.address は undefined になります。そこから city を取得しようとするとエラーになります。

これは「相手が住所を教えてくれるとは限らない」という現実を反映しています。外部から受け取るデータは、期待通りの形式でない可能性が常にあります。

このエラーが発生すると、アプリケーションがその場でクラッシュし、画面が真っ白になったり、処理が途中で止まったりします。ユーザーにとってはサービスが使えなくなる深刻な問題につながります。

モダンな対策方法

問題がきちんと理解できたところで、実際にどのように対処すればいいかを紹介します。

Optional Chaining(オプショナルチェイニング)

?. を使うと、途中でundefinedやnullがあっても安全にアクセスできます。「安全にアクセスできる」とは、undefinedやnullの場合でもエラーを発生させずに処理を続けられるという意味です。

// 従来の書き方
if (user && user.address && user.address.city) {
  console.log(user.address.city);
}

// Optional Chainingを使った書き方
console.log(user?.address?.city);  // undefinedが返る(エラーにならない)

?. は「もし値があれば次に進む、なければundefinedを返す」という動作をします。

Nullish Coalescing(NULL合体演算子)

?? を使うと、nullまたはundefinedの場合にデフォルト値を設定できます。日本語では「Null合体演算子」と呼ばれることもあります。

// ユーザー名がなければ「ゲスト」を使う
const displayName = user?.name ?? 'ゲスト';
console.log(displayName);  // ユーザー名があればそれを、なければ「ゲスト」を表示

|| との違い

似た演算子に || がありますが、動作が異なります。

const count = 0;

// || は 0 や 空文字 もfalseとして扱う
console.log(count || 10);  // 10 (0がfalseとして扱われた)

// ?? は null と undefined だけを対象にする
console.log(count ?? 10);  // 0 (0はそのまま使われる)

「0」や「空文字」を有効な値として扱いたい場合は ?? を使います。

実践的なチェックパターン

ここまでの内容を踏まえて、実際によく使うチェックパターンを紹介します。

配列の安全なアクセス

const posts = response?.data?.posts ?? [];

// 投稿があれば表示、なければメッセージを表示
if (posts.length > 0) {
  posts.forEach(post => console.log(post.title));
} else {
  console.log('投稿がありません');
}

関数の引数チェック

function greet(name) {
  // 引数がない場合のデフォルト値
  const displayName = name ?? 'ゲスト';
  console.log(`こんにちは、${displayName}さん!`);
}

greet('田中');    // こんにちは、田中さん!
greet();         // こんにちは、ゲストさん!
greet(null);     // こんにちは、ゲストさん!

参考情報

Optional ChainingとNullish Coalescingは、ES2020(2020年)でJavaScriptに追加された比較的新しい機能です。モダンなブラウザやNode.js 14以降であれば問題なく使えます。

nullとundefinedのチェックは、エラーを防ぐための「防御的プログラミング」の基本です。ただし、どこでもチェックを入れればいいわけではありません。null対策には他にもTypeScriptのstrict modeやResult型パターンなどがあります。詳しくは以下の記事も参考にしてください。