【JavaScript】配列が空かどうかのチェック方法と判定

はい、承知いたしました。JavaScriptで配列が空かどうかをチェックする方法と判定に関する、詳細な説明を含む記事を作成します。約5000語を目指し、様々な側面から解説します。


【JavaScript】配列が空かどうかのチェック方法と判定:詳細解説とベストプラクティス

はじめに:なぜ配列の空チェックが重要なのか?

JavaScriptを用いた開発において、配列は最も頻繁に使用されるデータ構造の一つです。データのリストを表したり、コレクションを扱ったりする際に不可欠です。しかし、配列を扱う上で常に考慮しなければならない重要な点があります。それは、「その配列に要素が含まれているか、つまり空ではないか?」という問題です。

APIからのレスポンスデータ、ユーザーからの入力、計算結果など、様々なソースから配列を受け取る可能性があります。これらの配列が、期待どおりに要素を持っているか、あるいは全く要素を持っていない「空」の状態であるかによって、その後のプログラムの振る舞いを制御する必要が出てきます。

例えば:

  • データの表示: 配列が空の場合、リストを表示する代わりに「データがありません」というメッセージを表示したい。
  • 処理の分岐: 配列に要素がある場合にのみ、その要素を使った複雑な計算やデータ処理を実行したい。空の場合は処理をスキップしたい。
  • エラーハンドリング: 配列が期待される場所に空でない配列が来た場合、あるいは配列であることすら保証されない場合に備えたい。
  • パフォーマンス: 空の配列に対して無駄なループ処理や反復処理を行わないようにしたい。

このように、配列が空であるかどうかを正確に判定することは、プログラムのロバスト性(堅牢性)、効率性、そしてユーザーエクスペリエンスの向上に不可欠です。

しかし、一見単純に見えるこの「配列の空チェック」も、JavaScriptの動的な性質や多様な値の可能性を考慮すると、いくつかの方法があり、それぞれに利点や注意点が存在します。特に、変数が本当に配列であるかどうかの確認、あるいは nullundefined のような「配列ではない」状態をどのように扱うかによって、適切なチェック方法が異なります。

この記事では、JavaScriptで配列が空であるかを判定するための様々な方法を、最も一般的かつ推奨されるアプローチから始め、代替方法、潜在的な落とし穴、パフォーマンスの考慮、そして実用的なベストプラクティスに至るまで、徹底的に解説します。

読者の皆さんが、自信を持って、そして堅牢に配列の空チェックを行えるようになることを目指します。

1. 配列の長さを利用する:最も一般的で推奨される方法

JavaScriptの配列オブジェクトは、その配列に含まれる要素の数を示す length というプロパティを持っています。空の配列([])の length0 です。要素が1つでも含まれていれば、length は1以上の値になります。

この性質を利用するのが、最もシンプルで直接的、かつ一般的に推奨される配列の空チェック方法です。

基本的なコード:

“`javascript
let myArray1 = []; // 空の配列
let myArray2 = [1, 2, 3]; // 要素を持つ配列
let myArray3 = [“a”, “b”]; // 要素を持つ配列

if (myArray1.length === 0) {
console.log(“myArray1 は空の配列です。”); // この行が実行される
} else {
console.log(“myArray1 は空ではありません。”);
}

if (myArray2.length === 0) {
console.log(“myArray2 は空の配列です。”);
} else {
console.log(“myArray2 は空ではありません。”); // この行が実行される
}

if (myArray3.length === 0) {
console.log(“myArray3 は空の配列です。”);
} else {
console.log(“myArray3 は空ではありません。”); // この行が実行される
}
“`

なぜ length プロパティが適しているのか?

  1. 直接的で明確: length プロパティは配列の「サイズ」を直接的に表します。length === 0 は、「要素が0個である」という意味をそのままコードに反映しているため、非常に分かりやすいです。
  2. 効率的: JavaScriptエンジンは、配列の length プロパティを非常に効率的に管理しています。要素の追加や削除が行われるたびに自動的に更新され、その値を取得する操作は非常に高速です(通常、O(1)の時間計算量)。配列の要素数に関わらず、常に一定の時間で判定できます。
  3. 信頼性: length プロパティは、配列オブジェクトの標準的なプロパティであり、その挙動はECMAScript仕様で明確に定義されています。予期せぬ副作用やブラウザ/環境による差異の心配がほとんどありません。
  4. 広く使われている: ほとんどのJavaScript開発者がこの方法を知っており、利用しています。コードの可読性やメンテナンス性が高まります。

短縮形:!arr.length を使用する

length プロパティは数値です。JavaScriptでは、数値の 0 は論理値の false に評価され、それ以外の数値(正の数、負の数)は true に評価されます。この性質を利用して、arr.length === 0!arr.length と短縮して書くことがあります。

!arr.length は、arr.length の真偽値を反転させたものです。

  • arr.length0 の場合、!0!false となり true に評価されます。
  • arr.length0 以外(つまり、1以上の要素がある)の場合、!arr.length!true となり false に評価されます。

したがって、if (!arr.length) は「もし配列が空ならば」という意味になります。

短縮形のコード:

“`javascript
let myArray = [];

if (!myArray.length) {
console.log(“myArray は空です(短縮形)。”); // この行が実行される
}

let anotherArray = [1];

if (!anotherArray.length) {
console.log(“anotherArray は空です(短縮形)。”);
} else {
console.log(“anotherArray は空ではありません(短縮形)。”); // この行が実行される
}
“`

!arr.length の利点:

  • コードが簡潔になる。

!arr.length の注意点:

  • arr.length === 0 に比べると、初心者には直感的に分かりにくいかもしれません。
  • 技術的には、length プロパティが nullundefined、あるいは非数値に評価されるオブジェクト(ただし、これは標準的な配列では起こりえません)であった場合、予期せぬ挙動をする 可能性 がゼロではありませんが、これは一般的な配列の利用においては無視できるレベルのリスクです。標準的な配列オブジェクトに対しては、length === 0 と全く同じ論理的な結果をもたらします。

結論として、arr.length === 0 は最も推奨される、分かりやすく堅牢な方法です。コードの簡潔さを優先する場合や、チーム内で共通のスタイルとなっている場合は !arr.length も有効な選択肢です。どちらの方法も、チェック対象が確実に配列であるという前提の下で非常に効率的に機能します。

2. nullundefined の可能性を考慮する

前述の length プロパティを利用する方法は、対象が「配列である」という前提の下で最適に機能します。しかし、現実のプログラムでは、配列を受け取るつもりの変数が、実際には nullundefined、あるいは全く別の型の値(文字列、数値、オブジェクトなど)である可能性があります。

これらの非配列型の値に対して .length プロパティにアクセスしようとすると、多くの場合TypeErrorが発生し、プログラムがクラッシュします。

“`javascript
let data = null;

// data.length にアクセスしようとするとエラー (TypeError)
// if (data.length === 0) { … } // ダメ!

let result; // undefined

// result.length にアクセスしようとするとエラー (TypeError)
// if (result.length === 0) { … } // ダメ!

let text = “hello”;

// text.length はエラーにはならないが、5になる。意図しない結果。
if (text.length === 0) {
console.log(“空の文字列ですが、配列の空チェックとしては不適切です。”);
}

let obj = { a: 1 };

// obj.length は undefined になることが多い(lengthプロパティがなければ)。
// undefined === 0 は false。意図しない結果。
if (obj.length === 0) {
console.log(“オブジェクトですが、配列の空チェックとしては不適切です。”);
}
“`

したがって、対象の変数が配列であることが保証されない場合は、まず null または undefined であるかを確認する必要があります。

null および undefined のチェック

変数が null または undefined であるかをチェックするには、以下の方法があります。

  • 厳密等価演算子 (===) または不等価演算子 (!==) を使用する:
    “`javascript
    let potentialArray = getSomeData(); // この関数が配列、null、または undefined を返す可能性があると仮定

    if (potentialArray === null || potentialArray === undefined) {
    console.log(“potentialArray は null または undefined です。配列ではありません。”);
    // このケースを「空」と見なすか、それとも「無効な入力」と見なすかは、プログラムのロジックによります。
    // 配列処理を進めることはできません。
    } else {
    // ここでは potentialArray は null でも undefined でもないことが保証される
    // しかし、まだ配列であるかは不明です
    console.log(“potentialArray は null でも undefined でもない値です。”);
    }
    “`

  • 抽象等価演算子 (==) または不等価演算子 (!=) を使用する:
    JavaScriptでは、== nullnullundefined の両方にマッチするという特殊な挙動をします(Nullish Coalescing (??) と似ていますが、少し異なります)。この性質を利用すると、null || undefined のチェックを短縮できます。

    “`javascript
    let potentialArray = getSomeData();

    if (potentialArray == null) { // == null は potentialArray が null または undefined の場合に true
    console.log(“potentialArray は null または undefined です。”);
    // 配列処理に進めない
    } else {
    console.log(“potentialArray は null でも undefined でもない値です。”);
    // まだ配列であるかは不明
    }
    ``== nullの使用は好みが分かれますが、この特定のケースでは広く認識されているイディオムであり、簡潔なコードになります。可読性を重視する場合や、厳密等価性を原則とするコーディング規約に則る場合は、=== null || === undefined` の形式を使用します。

3. Array.isArray() を使用して型をチェックする

nullundefined でないことを確認した後も、その変数が本当に配列であるかを確認する必要があります。前述のように、文字列やオブジェクトなど、他の型の値も length プロパティを持つ可能性があるからです。

Array.isArray() メソッドは、引数として渡された値が配列である場合に true を返し、そうでない場合に false を返すために特別に設計されています。これはECMAScript 5で導入された標準的なメソッドであり、最も信頼性の高い配列判定方法です。

コード例:

“`javascript
let maybeArray1 = [1, 2, 3];
let maybeArray2 = “hello”;
let maybeArray3 = { length: 0 };
let maybeArray4 = null;
let maybeArray5 = undefined;
let maybeArray6 = [];

console.log(Array.isArray(maybeArray1)); // true
console.log(Array.isArray(maybeArray2)); // false
console.log(Array.isArray(maybeArray3)); // false (lengthプロパティを持つオブジェクトだが配列ではない)
console.log(Array.isArray(maybeArray4)); // false
console.log(Array.isArray(maybeArray5)); // false
console.log(Array.isArray(maybeArray6)); // true
“`

Array.isArray() は、typeof 演算子よりも配列の判定において優れています。なぜなら、typeof []"object" を返すため、通常のオブジェクトと区別できないからです。

4. 堅牢な空配列チェックの組み合わせ

これまでの要素(null/undefined チェック、Array.isArray() チェック、length === 0 チェック)を組み合わせることで、どのような値が渡されても安全かつ正確に「配列であり、かつ空である」かを判定する堅牢なチェックが実現できます。

最も一般的なロジックは以下のようになります。

  1. 渡された変数が null または undefined でないことを確認する。
  2. 渡された変数が Array.isArray() によって配列であると判定されることを確認する。
  3. その配列の length プロパティが 0 であることを確認する。

これらの条件がすべて満たされた場合にのみ、「空の配列である」と判定します。

堅牢なチェックのコード例:

“`javascript
function isStrictlyEmptyArray(arr) {
// 1. null または undefined でないことを確認 (== null を使用して両方を効率的にチェック)
// あるいは厳密に arr !== null && arr !== undefined でも良い
if (arr == null) {
return false; // null や undefined は空の配列ではない
}

// 2. Array.isArray() で配列であることを確認
if (!Array.isArray(arr)) {
return false; // 配列でないものは空の配列ではない
}

// 3. length が 0 であることを確認
return arr.length === 0;
}

// テスト
console.log(“— 堅牢なチェック —“);
console.log([]: ${isStrictlyEmptyArray([])}); // true
console.log([1, 2]: ${isStrictlyEmptyEmptyArray([1, 2])}); // false
console.log(null: ${isStrictlyEmptyArray(null)}); // false
console.log(undefined: ${isStrictlyEmptyArray(undefined)}); // false
console.log("hello": ${isStrictlyEmptyArray("hello")}); // false
console.log({}: ${isStrictlyEmptyArray({})}); // false
console.log({ length: 0 }: ${isStrictlyEmptyArray({ length: 0 })}); // false
console.log("": ${isStrictlyEmptyArray("")}); // false (空文字列)
console.log(0: ${isStrictlyEmptyArray(0)}); // false
“`

この isStrictlyEmptyArray 関数は、「渡された値が『配列』という型であり、かつその配列が『空』である」という非常に厳密な判定を行います。

「要素がないコレクション」としての空判定

プログラムによっては、「渡された値が null でも undefined でもなく、配列であればその配列が空、配列でなければ(他の型の値であれば)それは処理を進められない『空のような状態』と見なす」というロジックが必要な場合もあります。これは、「配列として扱おうとしたときに、要素を取り出せるかどうか」を判定するようなシナリオで役立ちます。

このような「要素がないコレクション(または処理できない入力)」としての空判定は、以下のように実装できます。

“`javascript
function isEmptyCollectionOrInvalid(arr) {
// null または undefined であれば、要素がない状態と見なす
if (arr == null) {
return true;
}

// 配列であれば、その length が 0 かどうかで判定
if (Array.isArray(arr)) {
return arr.length === 0;
}

// 配列でない場合は、要素がない状態と見なす(または無効な入力として true を返す)
// ここでの解釈は文脈による。ここでは「要素がないものとして扱う」と定義する。
// 例外を投げる選択肢もあるが、ここでは boolean を返す関数とする。
// 他の型でも length を持つ場合があるが、ここでは配列以外は要素がないものとして扱うシンプルなケースを想定。
// もし文字列やargumentsオブジェクトなどの length を持つ他の型も「空でない」と見なすなら、ロジックはより複雑になる。
// シンプルに「配列として期待していたが、そうではなかった、だから空と同じように扱う」というロジック。
return true; // 配列ではなく、null/undefinedでもない -> コレクションとしては空ではないが、ここでは要素がない状態と見なす
}

// この関数は、文脈によって返す値を調整する必要がある場合があります。
// 例えば、「nullやundefined、または配列でない場合はfalse(=処理できない)」とし、
// 「空の配列の場合のみtrue」とするなら、isStrictlyEmptyArray 関数がそのまま使えます。

// ここでは「配列のように扱いたいが、要素があるか不明。要素がなければ何もしたくない」というケースを想定し、
// null, undefined, または空配列をまとめて「要素がない」と判定する例とします。
// ただし、この解釈は一般的ではないかもしれないため、注意が必要です。

// より一般的な「処理可能でかつ空ではないか?」というチェックは以下のようになります。
function isProcessableAndNotEmptyArray(arr) {
return Array.isArray(arr) && arr.length > 0;
}

// または、その逆「処理可能だが空であるか?」
function isProcessableAndEmptyArray(arr) {
return Array.isArray(arr) && arr.length === 0;
}

// そして「そもそも処理可能な配列であるか?」
function isProcessableArray(arr) {
return Array.isArray(arr);
}

// これらの関数を組み合わせたり、あるいは最初の isStrictlyEmptyArray を使うのが最も一般的でしょう。
// isStrictlyEmptyArray は「arrが空の配列オブジェクトそのものであるか」を判定します。
// isProcessableAndNotEmptyArray は「arrが少なくとも1つ以上の要素を持つ配列であるか」を判定します。

// 文脈に応じて、これらの定義を明確にすることが重要です。
// 通常、「配列が空かどうかのチェック」と言った場合は、isStrictlyEmptyArray または isProcessableAndEmptyArray に近い意味合いであることが多いです。
// null/undefinedや非配列をどう扱うかは、そのチェックを行うコードの直前で判定するか、関数内でまとめて判定するかを決めます。
“`

最も一般的で実用的なパターン:

実際のアプリケーションコードで最も頻繁に見られるパターンは、「渡された変数が有効な(null/undefined ではなく)配列である場合に、それが空かどうかを判定する」というものです。これは、前述の要素を組み合わせることで実現できます。

“`javascript
let receivedData = fetchData(); // API呼び出しなど、何らかのデータ取得処理

if (receivedData == null) {
console.log(“データは null または undefined です。処理できません。”);
// エラーハンドリングやデフォルト値の設定など
} else if (!Array.isArray(receivedData)) {
console.log(“データは配列ではありません。処理できません。”);
// エラーハンドリングや型変換の試みなど
} else {
// ここでは receivedData は確実に配列であることが保証される
if (receivedData.length === 0) {
console.log(“データは空の配列です。要素がありません。”);
// 例: 「データがありません」と表示する
} else {
console.log(データは空ではありません。要素数は ${receivedData.length} です。);
// 例: 配列の要素を使ってリストを表示したり、集計処理を行ったりする
// receivedData.forEach(…)
}
}
``
このパターンは、異なる種類の入力(
null,undefined`, 非配列, 空配列, 非空配列)に対して明確に分岐を定義できるため、非常に堅牢で分かりやすいです。

5. 非推奨または注意が必要なチェック方法

配列の空チェックに関して、いくつかの代替手段が考えられるかもしれませんが、それらは通常、非推奨であったり、特定の落とし穴があったりします。

a. arr == []arr === [] で比較する: 絶対にダメ!

新しい配列リテラル [] を作成し、チェックしたい配列 arr と比較する方法を思いつくかもしれません。

“`javascript
let myArray = [];

if (myArray === []) { // ダメな例
console.log(“myArray は空の配列リテラルと同じ参照ですか?”);
}

if (myArray == []) { // もっとダメな例
console.log(“myArray は空の配列リテラルと抽象的に等価ですか?”);
}
“`

なぜこれがダメなのか?

JavaScriptでは、配列はオブジェクトです。オブジェクトの比較演算子(== または ===)は、プリミティブ型の値の比較とは異なり、「値が等しいか」ではなく「同じオブジェクトへの参照を持っているか」をチェックします。

上記のコードで [] と記述するたびに、JavaScriptエンジンはメモリ上に全く新しい配列オブジェクトを作成します。したがって、たとえ両方の配列が要素を一つも持たない空の配列であったとしても、それらはメモリ上の異なる場所にある、異なるオブジェクトです。

“`javascript
let arr1 = [];
let arr2 = [];

console.log(arr1 === arr2); // false (異なるオブジェクト参照)
console.log(arr1 == arr2); // false (異なるオブジェクト参照)

let arr3 = arr1; // arr3 に arr1 と同じ参照を代入
console.log(arr1 === arr3); // true (同じオブジェクト参照)
“`

したがって、arr === []arr == []true になるのは、arrたまたまその場で作成された新しい空の配列リテラルと全く同じメモリ参照を指しているという、非常に稀で通常は期待しない状況に限られます。これは配列の空チェックとして機能しません。

b. JSON.stringify(arr) === '[]' を使用する:非効率で推奨されない

配列をJSON文字列に変換し、それが "[]" と等しいかどうかをチェックする方法も考えられます。

“`javascript
let myArray = [];
let anotherArray = [1];
let notAnArray = null;

console.log(JSON.stringify(myArray) === ‘[]’); // true
console.log(JSON.stringify(anotherArray) === ‘[]’); // false
console.log(JSON.stringify(notAnArray) === ‘[]’); // false (nullは”null”になる)
console.log(JSON.stringify(undefined) === ‘[]’); // false (undefinedはJSON.stringifyで変換するとundefinedになる)
console.log(JSON.stringify({}) === ‘[]’); // false (オブジェクトは”{}”になる)
“`

この方法は、一見空の配列を判定できているように見えますが、いくつかの欠点があります。

欠点:

  1. 非効率: JSON.stringify() は配列全体を走査し、その内容に基づいて文字列を生成する処理です。配列の要素数が多い場合、length プロパティへのアクセス(O(1))に比べてはるかに多くの時間とリソースを消費します。配列が空であるかどうかだけを知りたい場合に、配列全体を文字列化するのは無駄な処理です。
  2. 非配列に対する挙動の不一致: nullundefined に対してはエラーにはなりませんが、null"null"undefinedundefined (文字列化の結果として)となり、"[]" とは比較できない結果になります。他の型の値に対しては、予期しない文字列になる可能性があります。結局、この方法を使う場合でも、事前に null/undefinedArray.isArray() によるチェックが必要になります。
  3. 要素の内容に依存しないはずのチェックが文字列表現に依存する: 理論的には、配列が空であるかどうかは、その要素の内容に依存すべきではありません。しかし、JSON.stringify は要素の型や値によって結果が変わる可能性があり、より複雑なケース(例えば、配列内に循環参照オブジェクトが含まれる場合など)で予期しないエラーや結果を引き起こす可能性があります。

結論として、JSON.stringify(arr) === '[]' は、arr.length === 0 というシンプルで効率的な代替手段がある以上、避けるべき方法です。

c. 配列のメソッド(every, some, find など)を利用する:不適切

配列のメソッド (every, some, find など) は、配列に要素が含まれている場合にのみコールバック関数を実行します。空の配列に対してこれらのメソッドを呼び出しても、コールバック関数は一度も実行されず、即座に特定の値を返します(everytruesomefalsefindundefined など)。

この性質を利用して、これらのメソッドの返り値をチェックすることで、配列が空であるかを判定できる かもしれません

“`javascript
let myArray = [];
let anotherArray = [1];

// every は空配列に対して true を返す
console.log(myArray.every(() => false)); // true
console.log(anotherArray.every(() => false)); // false

// some は空配列に対して false を返す
console.log(myArray.some(() => true)); // false
console.log(anotherArray.some(() => true)); // true
“`

なぜこれが不適切なのか?

  1. 意図が不明確: arr.every(() => false) が「配列が空である」ことを意味するというコードは、その意図が非常に分かりにくいです。可読性が著しく低下します。
  2. 非効率: これらのメソッドも内部的には配列を(少なくとも要素がないことを確認するために)走査するオーバーヘッドを持つ可能性があります。length プロパティへのアクセスよりも効率的ではありません。
  3. null/undefined/非配列には使えない: これらのメソッドは配列オブジェクトに対して呼び出す必要があり、nullundefined に対してはエラーになります。結局、事前のチェックが必要です。

これらのメソッドは、配列の「要素の内容」に関するチェックを行うために設計されています。配列が空であるかという「配列の構造」に関するチェックには、length プロパティを使うのが適切です。

6. スパース配列(Sparse Arrays)と length

JavaScriptの配列は、必ずしも全てのインデックスに要素を持つとは限りません。要素が設定されていないインデックスが存在する配列を「スパース配列(Sparse Array)」と呼びます。

例:
javascript
let sparseArray = [,"a",,"b"]; // インデックス0と2に要素がない
console.log(sparseArray.length); // 4
console.log(sparseArray[0]); // undefined
console.log(sparseArray[1]); // "a"
console.log(sparseArray[2]); // undefined
console.log(sparseArray[3]); // "b"

この例では、実際に要素が設定されているのは2つだけですが、length プロパティは 4 を返します。これは、length が実際に格納されている要素の数ではなく、「配列の最大のインデックス値 + 1」を基に決定されるためです。

arr.length === 0 というチェックは、このようなスパース配列に対しても正しく機能します。なぜなら、空の配列は最大のインデックスを持たず、length0 に初期化されるからです。

ただし、スパース配列の場合に「実際に要素が格納されているか」をチェックしたい場合は、length だけでは不十分です。その場合は、ループ処理や filter() などのメソッドを使って undefined や「スロットが空」の状態を除外する必要があります。

例:実際に値が格納されている要素数を知りたい場合
javascript
let sparseArray = [,"a",,"b"];
let actualElementsCount = sparseArray.filter(element => element !== undefined).length; // 注意:これは undefined 値そのものと空スロットを区別しない単純な例
console.log(actualElementsCount); // 2

しかし、「配列が空かどうかのチェック」という文脈では、通常「length が0であるか」を意味します。スパース配列であっても、length が0であればそれは「空の配列」と見なされます。つまり、length === 0 という方法はスパース配列に対しても期待通りに機能します。

7. パフォーマンスの考慮

ほとんどのウェブアプリケーションやNode.jsアプリケーションにおいて、配列の空チェックのパフォーマンスはボトルネックになることは稀です。しかし、非常にパフォーマンスが要求される場面(例:数万回繰り返されるループ内、リアルタイム処理など)では、わずかな差も重要になることがあります。

  • arr.length === 0 または !arr.length: これは最も高速な方法です。length プロパティの取得は非常に効率的であり、その後の数値比較も高速です。JavaScriptエンジンの最適化も進んでいます。時間計算量は O(1) です。
  • Array.isArray(): このメソッドは、引数の内部的な型情報をチェックするため、非常に高速です。length の取得と同等、あるいはごくわずかに遅い程度の速度です。時間計算量は O(1) です。
  • null/undefined チェック: プリミティブ値の比較であるため、非常に高速です。時間計算量は O(1) です。
  • JSON.stringify(arr) === '[]': 配列の要素数に比例して時間がかかります。配列が大きくなるほど遅くなります。時間計算量は配列の要素数に対して少なくとも O(N) になります。空の配列の場合も、文字列化のオーバーヘッドがあります。
  • 配列メソッド (every, some など): 空配列に対してはコールバックが実行されないため一見速そうですが、メソッド呼び出しと内部処理のオーバーヘッドがあります。length チェックに比べて遅いです。非空配列の場合は要素数に比例して時間がかかります。

結論として、パフォーマンスの観点からも、arr.length === 0 または !arr.length を使用し、必要に応じて null/undefinedArray.isArray() による事前の型チェックを行うのが最善です。 JSON.stringify や配列メソッドを空チェックのためだけに使用することは、パフォーマンスの無駄であり、非推奨です。

8. ベストプラクティスと推奨されるアプローチ

これまでの議論を踏まえ、JavaScriptで配列の空チェックを行うためのベストプラクティスは以下のようになります。

シナリオ1:変数が確実に配列であると分かっている場合

変数 arr がすでに nullundefined ではなく、かつ Array.isArray(arr)true であることが論理的に保証されている状況(例えば、関数が「配列のみを受け取る」と明確に定義されており、呼び出し元でそれが守られている場合など)。

“`javascript
// arr は確実に配列であることが分かっている前提
if (arr.length === 0) {
console.log(“配列は空です。”);
} else {
console.log(“配列は空ではありません。”);
}

// または短縮形
if (!arr.length) {
console.log(“配列は空です。”);
} else {
console.log(“配列は空ではありません。”);
}
“`
これは最もシンプルで効率的なケースです。

シナリオ2:変数が配列かもしれないし、nullundefined、または別の型かもしれない場合

APIからのレスポンス、関数からの戻り値、ユーザー入力など、変数の型や値が不明確な状況。これが最も一般的な現実世界のシナリオです。

この場合、安全のために以下のステップでチェックを行います。

  1. null または undefined でないか?
  2. Array.isArray() で配列であるか?
  3. 配列であれば、length === 0 で空であるか?

これを組み合わせたコードは以下のようになります。

“`javascript
let possibleArray = fetchDataFromAPI(); // この値が何であるか不明

if (possibleArray == null) {
// null または undefined の場合の処理
console.log(“値は null または undefined です。配列として扱えません。”);
// 例: エラーメッセージ表示、デフォルト処理
} else if (!Array.isArray(possibleArray)) {
// 配列ではない場合の処理
console.log(値は配列ではありません (型: ${typeof possibleArray})。配列として扱えません。);
// 例: エラーメッセージ表示、ログ記録
} else {
// ここに到達した場合、possibleArray は確実に配列である
if (possibleArray.length === 0) {
// 空の配列だった場合の処理
console.log(“値は空の配列です。要素がありません。”);
// 例: 「データなし」表示
} else {
// 要素を持つ配列だった場合の処理
console.log(値は要素数 ${possibleArray.length} の配列です。);
// 例: リスト表示、データ処理
}
}
“`
このチェックの連鎖は、あらゆる可能性のある入力値に対して安全かつ適切な処理を分岐させることができます。

ヘルパー関数の利用

上記の堅牢なチェックロジックは頻繁に使用される可能性があるため、再利用可能なヘルパー関数として切り出すと、コードの重複を防ぎ、可読性を向上させることができます。

例えば、「渡された値が『空の配列』と見なせる状態か?」を判定する関数を考えます。ここでの「空の配列と見なせる状態」の定義は、文脈によって異なります。

定義1:「厳密に、配列オブジェクトであり、かつ空である」

“`javascript
/
* 値が厳密に「空の配列」であるか判定します。
* null, undefined, 非配列は false と判定されます。
* @param {
} value – チェック対象の値
* @returns {boolean} – 空の配列であれば true、そうでなければ false
/
function isStrictlyEmptyArray(value) {
return Array.isArray(value) && value.length === 0;
}

// 使用例
console.log(“— isStrictlyEmptyArray —“);
console.log([]: ${isStrictlyEmptyArray([])}); // true
console.log([1, 2]: ${isStrictlyEmptyArray([1, 2])}); // false
console.log(null: ${isStrictlyEmptyArray(null)}); // false
console.log(undefined: ${isStrictlyEmptyArray(undefined)}); // false
console.log("hello": ${isStrictlyEmptyArray("hello")}); // false
``
この関数は、前述の堅牢なチェックロジックの一部を切り出したものです。
null/undefinedや非配列の場合はfalse` を返すため、呼び出し側でこれらのケースを別途ハンドリングする必要があります。これは、受け取った値が「処理可能な配列だが、その内容が空か?」を問いたい場合に便利です。

定義2:「要素がないコレクション(null, undefined, または空の配列)」

これは少し特殊な定義ですが、「もしこれがリストやコレクションだと仮定したら、中に要素はありますか?」という問いに答えるイメージです。nullundefined はリストとして扱えないため、要素がない状態と見なします。空の配列も要素がない状態です。要素を持つ配列だけが「要素がある」と判定されます。

“`javascript
/
* 値が「要素がないコレクション」と見なせる状態かを判定します。
* null, undefined, 空の配列は true と判定されます。
* 要素を持つ配列や、配列以外の値(文字列、オブジェクトなど)は false と判定されます。
* @param {
} value – チェック対象の値
* @returns {boolean} – 要素がなければ true、要素があれば false
/
function isEmptyCollection(value) {
// null または undefined であれば、要素がないと見なす
if (value == null) {
return true;
}
// 配列であれば、length が 0 なら要素がない
if (Array.isArray(value)) {
return value.length === 0;
}
// 配列以外の値は、コレクションとしては扱えないため、ここでは要素がない状態と見なす(文脈による)
// より一般的なのは、配列以外は false とする(つまり、要素があるかどうかの判定対象外とする)
// 例:文字列やargumentsオブジェクトは length を持つため、「要素がある」と見なしたい場合もある
// ここではシンプルなケースとして、配列以外は「コレクションとして要素がない」と見なすか、
// あるいは「コレクションとして扱えるものではない」として false を返すかの選択肢があります。
// より安全で一般的なのは「コレクションとして扱える配列であり、かつ空であるか」を問う isStrictlyEmptyArray です。
// この isEmptyCollection は、特定のアプリケーションロジックでのみ有用な場合があります。
// 通常は isStrictlyEmptyArray と、null/undefined/非配列のチェックを分けて行う方が明瞭です。

// あえて isEmptyCollection を定義するなら、以下のどちらかの意図が多いでしょう:
// 意図A: null/undefined も含めて「処理対象のデータがない」か?
// return value == null || (Array.isArray(value) && value.length === 0);
// -> 要素がない状態と見なす: null, undefined, []
// -> 要素がある状態と見なす: [1,2], “hello”, {a:1} など配列以外の非null/undefined値

// 意図B: 配列ライクなものとして扱おうとしたら要素がなかったか? (null/undefinedや非配列は処理できないのでfalse)
// return Array.isArray(value) && value.length === 0;
// -> これは isStrictlyEmptyArray と同じになる。

// やはり isStrictlyEmptyArray が最も一般的で誤解がないでしょう。
// null/undefined チェックは呼び出し側で行うのが推奨パターンです。
// その上で、もし null/undefined/非配列を全て「空と同じ扱いにする」特定のロジックが必要なら、
// 以下のように書けますが、これは文脈を限定しないと混乱を招きます。
// return value == null || !Array.isArray(value) || value.length === 0; // これは「配列として扱えるものが空であるか、または配列として扱えないか」をまとめて true とする関数になる。

// 標準的な用途では、isStrictlyEmptyArray と null/undefined/非配列チェックの組み合わせがベストです。
// そのため、ヘルパー関数として切り出すなら isStrictlyEmptyArray が良いでしょう。

// この例の isEmptyCollection 関数は、配列以外の非null/undefined値をどう扱うかの定義が曖昧になりがちです。
// 例: isEmptyCollection(“”).length は0だが、”は配列ではない。
// 例: isEmptyCollection({}).length は undefined だが、{} は配列ではない。
// こうした曖昧さを避けるためにも、Array.isArray() は不可欠です。

// もし、本当に「要素があるか?」だけを知りたい(ただし配列を前提とする)なら、!isStrictlyEmptyArray(value) です。
// もし、null, undefined, または空配列の場合に true としたいなら:
return value == null || (Array.isArray(value) && value.length === 0);
// この関数名は isNullishOrEmptyArray とかの方が適切かもしれません。
}

// 使用例(isNullishOrEmptyArray として解釈)
console.log(“— isNullishOrEmptyArray (例としての isEmptyCollection) —“);
console.log([]: ${isEmptyCollection([])}); // true
console.log([1, 2]: ${isEmptyCollection([1, 2])}); // false
console.log(null: ${isEmptyCollection(null)}); // true
console.log(undefined: ${isEmptyCollection(undefined)}); // true
console.log("hello": ${isEmptyCollection("hello")}); // false (“hello”.length > 0 だが配列でない。この関数は「配列 or null/undefined」を前提とするため、この出力になる)
console.log({}: ${isEmptyCollection({})}); // false (配列でないため)
console.log({ length: 0 }: ${isEmptyCollection({ length: 0 })}); // false (配列でないため)
``
ヘルパー関数を作成する際は、「何をもって『空』と定義するか」を明確にし、関数名でその定義が伝わるようにすることが重要です。通常は、最初の
isStrictlyEmptyArrayのように、対象が「配列」であることを前提とした上で空かどうかを判定する関数が最も汎用的で安全です。そして、その関数を呼び出す前にnull/undefined`/非配列のチェックを行うのが、ロバストな実装パターンです。

9. まとめ:適切なチェック方法の選択

この記事では、JavaScriptで配列が空であるかを判定するための様々な方法を詳細に見てきました。

  • arr.length === 0:

    • 対象が確実に配列である場合に、最も推奨される、シンプル、効率的、かつ明確な方法です。
    • !arr.length と短縮することも可能ですが、可読性は arr.length === 0 の方が高い場合があります。
    • 時間計算量は O(1) です。
  • null / undefined チェック (arr == null):

    • 対象が配列でない可能性(特に nullundefined)がある場合に、.length アクセスによるエラーを防ぐために不可欠です。
    • == nullnullundefined の両方にマッチする便利なイディオムです。
  • Array.isArray(arr):

    • 対象が本当に配列であるかを信頼性高く判定するために不可欠です。typeof 演算子よりも優れています。
    • 時間計算量は O(1) です。
  • arr === [] / arr == []:

    • 絶対に使用してはいけません。 これはオブジェクト参照の比較であり、空の配列であるかを判定する目的には機能しません。
  • JSON.stringify(arr) === '[]':

    • 非推奨です。非効率であり、事前の型チェックが必要であり、文字列表現に依存するという欠点があります。
  • 配列メソッド (every, some など):

    • 空チェックの目的には不適切であり、意図が不明確で非効率です。

最も推奨されるパターン:

対象の変数の型が不明確な場合は、以下のロジックを組み合わせて使用するのが最も堅牢で安全です。

javascript
if (variable == null || !Array.isArray(variable) || variable.length === 0) {
// variable が null, undefined, 配列ではない、または空の配列である場合の処理
// 例:「データがありません」「処理対象がありません」といったメッセージ表示やデフォルト処理
} else {
// variable が要素を持つ配列である場合の処理
// 例: 配列の要素を使ったリスト表示やデータ処理
}

この組み合わせは、あらゆる種類の入力に対して適切に反応し、エラーを防ぎつつ、プログラムの意図を明確に表現できます。

特定の用途に合わせて、isStrictlyEmptyArray のようなヘルパー関数を定義し、コードの可読性と再利用性を高めることも有効です。

最終的に、どのチェック方法を選択するかは、チェック対象がどのような値である可能性があるか、そして「空」という状態をどのように定義するか(例:厳密に空の配列オブジェクトのみを指すのか、それとも nullundefined も含めて「要素がない状態」と見なすのか)によります。しかし、ほとんどのシナリオでは、Array.isArray()arr.length === 0 (および必要に応じた null/undefined チェック) を組み合わせるのが最も安全で効率的なアプローチです。

これらの知識を身につけることで、JavaScriptにおける配列の空チェックを自信を持って、そして堅牢に実装できるようになるでしょう。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール