【JavaScript】undefined・nullの扱い方でハマらないために

javascript-icon JavaScript

こんにちは、かつコーチです。

前回はエラーメッセージの読み方を解説しました。

その中でも何度も登場したnull、そしてよく似たundefined

この2つは見た目も意味も似ているようで、実は明確に役割が違います。

違いを理解しないままコードを書いていると、思わぬバグの温床になるので、今回はしっかり整理していきましょう。

undefinedとnullの違い

undefinedとは?

undefinedは、「値がまだ設定されていない」ことをJavaScript自身が自動的に割り当てる値です。

let userName;
console.log(userName); // undefined

変数を宣言しただけで値を入れていない場合、JavaScriptが自動的にundefinedを入れてくれます。

つまりundefinedは、プログラム側が「まだ何も入っていませんよ」と示すためのサインだと考えるとイメージしやすいです。

nullとは?

一方nullは、「値が空である」ことを開発者が意図的に代入する値です。

let selectedUser = null;
// まだユーザーが選択されていないことを、あえて明示している

nullは自動的には発生しません。

「ここには本来何か値が入るはずだが、今は意図的に空にしている」ということを、コードを書いた人間が明示するために使います。

2つの違いを一覧で整理する

undefinednull
発生のタイミングJavaScriptが自動で設定開発者が意図的に代入
意味合い値がまだ存在しない値が空であることの明示
型(typeof"undefined""object"(歴史的な仕様上の仕様)
よく登場する場面未定義の変数、存在しないプロパティ、返り値のない関数フォームの未選択状態、DOM要素が見つからない場合の返り値

typeof null"object"になるのは、JavaScript初期のバグがそのまま仕様として残ってしまった有名な話です。

「バグなのに直せなくなった」という背景を知っておくと、なぜこんな紛らわしい挙動になっているのか腑に落ちやすいと思います。

undefinedが登場する代表的な場面

プロパティが存在しないオブジェクト

オブジェクトに存在しないプロパティにアクセスすると、undefinedが返ります。

const user = { name: 'かつコーチ', age: 30 };
console.log(user.email); // undefined

エラーにはならず、静かにundefinedが返ってくるだけなので、気づかないまま処理を続けてしまいやすい点に注意が必要です。

返り値を書いていない関数

returnを書いていない関数は、undefinedを返します。

function greet(name) {
  console.log(`こんにちは、${name}さん`);
  // returnを書いていない
}

const result = greet('かつコーチ');
console.log(result); // undefined

greet関数自体は問題なく動いていますが、その返り値を別の変数に使おうとするとundefinedが紛れ込んでしまいます。

nullが登場する代表的な場面

DOM要素が見つからなかったとき

前回の記事でも触れましたが、document.querySelectorは該当する要素が見つからないとnullを返します。

const box = document.querySelector('.not-exist');
console.log(box); // null

undefinedではなくnullが返ってくる点が特徴で、「見つからなかった」ことを明示的に表しています。

意図的に値を空にしたいとき

「今は選択されていない」「まだデータが読み込まれていない」といった状態を表すために、開発者自身がnullを代入するケースもよくあります。

let currentUser = null;

function login(user) {
  currentUser = user;
}

loginが呼ばれるまでは、currentUserが「未ログイン状態」であることをコード上ではっきり示せます。

つまずきポイント:== での比較が思わぬ挙動を招く

かつコーチが実際にハマった話

私が過去に一番混乱したのが、undefinednull==で比較したときの挙動です。

フォームの入力チェックを実装していたときに、次のようなコードを書いていました。

❌ Before:==で比較して意図しない条件がtrueになる

function validateInput(value) {
  if (value == null) {
    console.log('入力値がありません');
    return false;
  }
  return true;
}

console.log(validateInput(undefined)); // false(想定通り)
console.log(validateInput(null));      // false(想定通り)
console.log(validateInput(0));         // true(想定通り)
console.log(validateInput(''));        // true(想定通り、ただし空文字も許容してしまう)

一見動いているように見えるのですが、実はこのコードは「value == nullundefinednullもどちらもtrueと判定してしまう」という==特有の性質に無意識に頼っていました。

チームメンバーからコードレビューで「これ、意図が分かりにくいから===で書き直そう」と指摘され、初めて自分が==の暗黙的な変換に頼りきっていたことに気づきました。

✅ After:意図を明示して===で個別に比較する

function validateInput(value) {
  if (value === undefined || value === null) {
    console.log('入力値がありません');
    return false;
  }
  return true;
}

console.log(validateInput(undefined)); // false
console.log(validateInput(null));      // false
console.log(validateInput(0));         // true
console.log(validateInput(''));        // true

===を使ってundefinednullを個別にチェックすることで、「この2つの状態を除外したい」という意図がコードを読んだだけで伝わるようになります。

value == nullという書き方自体は仕様として間違いではありませんが、チームで開発する場合は「なぜこの比較なのか」が伝わる書き方を優先する方が安全です。

undefined・nullを安全に扱うテクニック

Optional chaining(?.)で存在しないプロパティを安全に読む

存在しないかもしれないプロパティに安全にアクセスするには、?.(オプショナルチェイニング)が便利です。

const user = { name: 'かつコーチ' };

console.log(user.profile.age);
// Uncaught TypeError: Cannot read properties of undefined (reading 'age')

console.log(user.profile?.age);
// undefined(エラーにならない)

user.profileが存在しない場合、通常なら.ageを読もうとした時点でTypeErrorになりますが、?.を挟むことで「存在しなければundefinedを返して処理を止める」という安全な書き方ができます。

Nullish coalescing(??)でデフォルト値を設定する

undefinednullのときだけデフォルト値を使いたい場合は、??(Nullish coalescing)が便利です。

function showCount(count) {
  const displayCount = count ?? 0;
  console.log(`件数:${displayCount}`);
}

showCount(5);         // 件数:5
showCount(0);         // 件数:0(0はそのまま活かされる)
showCount(undefined); // 件数:0(デフォルト値が使われる)
showCount(null);      // 件数:0(デフォルト値が使われる)

似た用途に||(論理OR)もありますが、||0''のような「値としては存在するけどfalsyな値」までデフォルト値に置き換えてしまいます。

「数値の0はそのまま使いたいが、未入力のときだけデフォルト値にしたい」という場面では、??を使うのが安全です。

まとめ

この記事のポイント

  • undefinedはJavaScriptが自動で設定する「未定義」、nullは開発者が意図的に代入する「空」を表す
  • 存在しないプロパティへのアクセスや返り値のない関数の呼び出しはundefinedになる
  • document.querySelectorで見つからない場合や意図的な空状態はnullで表現されることが多い
  • ==による曖昧な比較に頼らず、===で意図を明示する書き方を心がける
  • ?.(Optional chaining)と??(Nullish coalescing)で安全に値を扱える

次に読むべき記事

undefinednullの扱いに慣れたら、次は非同期処理で特につまずきやすい罠を見ていきます。

→ 次の記事:非同期処理でハマりやすい罠と対処法

タイトルとURLをコピーしました