JavaScriptを学んでいると、必ずと言っていいほど「値がない」状態を表す2つの概念、nullとundefinedに遭遇します。
一見するとどちらも同じように見えますが、言語仕様上の役割やエンジニアが意図すべき使い分けのルールは明確に異なります。
本記事では、モダンな開発環境において、これらをどのように正しく扱い、バグの少ないコードを書くべきかを詳しく解説していきます。
初心者の方はもちろん、なんとなく使い分けている中級者の方も、改めて基礎を固める材料として活用してください。
undefinedの定義と発生条件
undefinedは、JavaScriptにおいて「変数が定義されているが、値が割り当てられていない状態」を指します。
これはJavaScriptの言語エンジンが自動的に付与する値であるという側面が強いのが特徴です。
変数の初期化忘れ
letやvarを使用して変数を宣言し、値を代入しなかった場合、その変数の初期値は自動的にundefinedになります。
let user;
console.log(user); // 初期化されていないためundefined
undefined
関数の戻り値
関数内でreturn文が記述されていない場合、またはreturnの後に値が指定されていない場合、その関数はundefinedを返します。
function greet() {
console.log("こんにちは");
// return文がない
}
const result = greet();
console.log(result);
こんにちは
undefined
存在しないプロパティへのアクセス
オブジェクトの中に存在しないプロパティに対してアクセスを試みた際にも、エラーではなくundefinedが返されます。
const member = { name: "田中" };
console.log(member.age); // ageプロパティは存在しない
undefined
nullの定義と役割
対してnullは、「値が存在しないことを明示的に示す」ために使用される特殊な値です。
undefinedがシステム側で自然に発生するものであるのに対し、nullは開発者が意図的に「ここは空である」と定義する際に利用されます。
意図的なリセット
例えば、以前は値が入っていたプロパティを空の状態に戻したい場合、nullを代入するのが一般的です。
let activeSession = { id: 101 };
// セッションを終了させるために意図的に空にする
activeSession = null;
console.log(activeSession);
null
APIレスポンスにおけるnull
データベースから取得したデータにおいて、該当する項目が「未入力」であることを示す場合にも、JSONデータとしてnullが返されることがよくあります。
これは「データ構造としては存在するが、中身は空である」という意図を明確にするためです。
nullとundefinedの決定的な違い
これら2つの値には、型や計算時の挙動においていくつかの重要な違いがあります。
以下の表で、主要な差異を整理してみましょう。
| 比較項目 | undefined | null |
|---|---|---|
| 意味 | 未定義(値が割り当てられていない) | 欠如(値が空であることを明示) |
| データの型 (typeof) | “undefined” | “object” |
| 数値変換時の値 | NaN | 0 |
| JSONでの扱い | キーごと削除されることがある | 値として保持される |
typeof演算子における注意点
JavaScriptの歴史的な仕様により、typeof nullを実行すると“object”が返されるというバグに近い挙動があります。
これはJavaScriptの初期設計におけるミスとされていますが、互換性の観点から現在も修正されていません。
console.log(typeof undefined);
console.log(typeof null);
"undefined"
"object"
等価演算子(==)と厳密等価演算子(===)
nullとundefinedを比較する場合、使用する演算子によって結果が変わります。
console.log(null == undefined); // 抽象的な比較では等しいとみなされる
console.log(null === undefined); // 厳密には型が異なるためfalse
true
false
実務では、意図しない挙動を防ぐために===を用いた厳密なチェックを行うことが推奨されます。
数値計算における挙動の違い
算術演算にこれらの値が紛れ込んだ場合、結果は大きく異なります。
nullは数値コンテキストでは0として扱われますが、undefinedはNaN(Not a Number)に変換されます。
const score = 10;
console.log(score + null); // 10 + 0 = 10
console.log(score + undefined); // 10 + NaN = NaN
10
NaN
この挙動の違いが原因で、計算処理においてバグが発生しやすいため注意が必要です。
モダンJavaScriptでの便利な扱い方
近年のJavaScript(ES2020以降)では、nullやundefinedをより安全に扱うための演算子が導入されています。
これらを活用することで、コードの可読性と堅牢性が飛躍的に向上します。
Null結合演算子 (??)
??演算子は、左辺がnullまたはundefinedの場合のみ、右辺の値を返します。
空文字(””)や数値の0を有効な値として保持したい場合に非常に便利です。
const config = {
timeout: 0,
theme: null
};
// || 演算子だと 0 が falsy なので 3000 になってしまう
const timeout = config.timeout ?? 3000;
// theme は null なので "light" が採用される
const theme = config.theme ?? "light";
console.log(timeout);
console.log(theme);
0
"light"
オプショナルチェイニング (?.)
ネストされたオブジェクトのプロパティにアクセスする際、途中の要素がnullまたはundefinedであってもエラーを投げずにundefinedを返してくれる機能です。
const response = {
data: {
user: null
}
};
// userがnullなので、addressにアクセスしようとしてもエラーにならずundefinedが返る
const zipCode = response.data.user?.address?.zipCode;
console.log(zipCode);
undefined
これまではif (response.data && response.data.user)のように長々と書いていたコードを簡潔に記述できます。
エンジニアが意識すべき使い分けの指針
実際の開発現場では、どのように使い分けるのが正解なのでしょうか。
プロジェクトの規約にもよりますが、一般的なベストプラクティスをいくつか紹介します。
1. 自分からundefinedを代入しない
値が空であることを示したい場合、コード内で自らundefinedを代入するのは避けましょう。
「値がセットされていない状態」と「明示的に空にした状態」の区別がつかなくなるためです。
「意図的に空にするならnullを使う」というルールを徹底するだけで、デバッグの効率が向上します。
2. 関数のデフォルト引数を活用する
関数に引数が渡されなかった場合、その値はundefinedになります。
デフォルト引数を使用すると、undefinedの場合にのみデフォルト値を適用できます。
function setup(mode = "standard") {
console.log(`現在のモード: ${mode}`);
}
setup(); // 引数がないので undefined -> "standard"
setup(undefined); // undefinedを渡しても "standard"
setup(null); // nullは「値がある」とみなされるため null
"現在のモード: standard"
"現在のモード: standard"
"現在のモード: null"
3. JSONでのシリアライズを考慮する
JSON.stringify()を使用してオブジェクトを文字列に変換する際、値がundefinedのプロパティは完全に削除されます。
一方で、値がnullのプロパティはそのまま保持されます。
const obj = {
key1: undefined,
key2: null
};
console.log(JSON.stringify(obj));
"{"key2":null}"
バックエンドとの通信で特定のキーを残しておく必要がある場合は、必ずnullを使用してください。
型チェックにおけるテクニック
変数が「nullかundefinedのどちらかであるか」を判定したいケースは非常に多いです。
その場合、あえて緩やかな比較(==)を利用する手法があります。
function processData(val) {
// valが null または undefined の場合にのみ真となる
if (val == null) {
console.log("処理可能なデータがありません");
return;
}
console.log("処理を開始します");
}
このように書くことで、val === null || val === undefinedという冗長な記述を避けることができます。
ただし、チーム開発では「厳密等価演算子(===)以外は禁止」というルールが設定されていることもあるため、プロジェクトの方針に合わせることが大切です。
TypeScriptとの親和性
2026年現在、JavaScript開発においてTypeScriptの利用はほぼ標準となっています。
TypeScriptでは、nullとundefinedを異なる型として厳密に区別します。
「strictNullChecks」オプションを有効にすることで、これらの値が原因で発生する実行時エラー(通称:億ドルの過ち)をコンパイル時に検知できるようになります。
型定義においてstring | nullと指定することで、「文字列が入るか、あるいは意図的に空であるか」という意思表示をコード上で明確に行えます。
まとめ
JavaScriptにおけるnullとundefinedは、どちらも「値がない」ことを表しますが、その背景にある意味合いは大きく異なります。
undefinedは「変数が未定義である」「初期化されていない」というシステム的な状態を指します。
一方でnullは「値が存在しないことを明示する」という開発者の意図的な宣言として使われます。
この違いを理解し、??演算子や?.演算子を適切に組み合わせることで、エラーに強く読みやすいコードを書くことができます。
最後に重要なポイントを振り返りましょう。
- 変数の初期値や関数のデフォルトの戻り値は
undefined。 - 「何もないこと」を明示的に代入する場合は
nullを選択する。 typeof nullは歴史的経緯で"object"になる。nullは数値の0、undefinedは数値のNaNとして扱われる。- モダンな開発では
??や?.を活用して安全にアクセスする。
これらの特性を正しく把握し、現場でのコーディングに活かしていきましょう。
