JavaScript Tips | 基本・共通ユーティリティ:基本判定・変換 – 空オブジェクト判定

JavaScript JavaScript
スポンサーリンク

空オブジェクトとは何かをまず整理する

JavaScript の「オブジェクト」は、キーと値のペアを入れておくための入れ物です。
その中に自分が定義したプロパティが 1 つもない {} の状態を、ここでは「空オブジェクト」と呼びます。

const a = {};                 // 空オブジェクト
const b = { id: 1 };          // プロパティあり

console.log(a);               // {}
console.log(b);               // { id: 1 }
JavaScript

ここで大事なのは、「空オブジェクト」は「オブジェクトという入れ物は存在しているが、中に自分で定義したプロパティが 1 つもない状態」だということです。
nullundefined のように「そもそも値がない」のとは意味が違います。


なぜ業務で空オブジェクト判定が必要になるのか

業務システムでは、「検索条件のオブジェクト」「更新するフィールドだけを詰めたパッチ用オブジェクト」「オプション設定のオブジェクト」など、
「中身があるかどうか」で処理を分けたいオブジェクトがたくさん出てきます。

例えば、検索条件を表すオブジェクトを考えます。

const conditions = {};  // ユーザーが何も条件を指定しなかった
JavaScript

このとき、「条件が 1 つも指定されていないなら、全件検索にする」「何か 1 つでも条件があれば、絞り込み検索にする」といった分岐を書きたくなります。
そのため、「このオブジェクトは空かどうか?」を判定するユーティリティは、実務でかなりよく使われます。


基本の空オブジェクト判定 Object.keys を使う

一番よく使われるのは、Object.keys で「自分が持っている列挙可能なプロパティのキー一覧」を取り、その配列の長さを見る方法です。

const obj1 = {};
const obj2 = { id: 1 };

console.log(Object.keys(obj1));        // []
console.log(Object.keys(obj2));        // ["id"]
console.log(Object.keys(obj1).length); // 0
JavaScript

これを使って、空オブジェクトかどうかを判定できます。

function isEmptyObject(value) {
  if (value === null) return false;
  if (typeof value !== "object") return false;
  return Object.keys(value).length === 0;
}

console.log(isEmptyObject({}));          // true
console.log(isEmptyObject({ id: 1 }));   // false
console.log(isEmptyObject(null));        // false
console.log(isEmptyObject([]));          // true(ここは後で深掘りします)
JavaScript

ここで重要なのは、typeof value === "object" だけでは不十分だということです。
nulltypeof null === "object" になってしまうので、先に value === null を除外しています。


「空オブジェクト」と「空配列」をどう区別するか

さきほどの isEmptyObject の例で、[](空配列)に対しても true になっているのに気づいたでしょうか。

console.log(Object.keys([]));        // []
console.log(Object.keys([]).length); // 0
JavaScript

配列もオブジェクトの一種なので、Object.keys([]) は空配列に対しても [] を返します。
つまり、「配列は空配列判定で扱いたい」「ここで言う“空オブジェクト”は、いわゆるプレーンなオブジェクトだけにしたい」という場合は、配列を除外する必要があります。

function isPlainEmptyObject(value) {
  if (value === null) return false;
  if (typeof value !== "object") return false;
  if (Array.isArray(value)) return false;
  return Object.keys(value).length === 0;
}

console.log(isPlainEmptyObject({}));        // true
console.log(isPlainEmptyObject({ id: 1 })); // false
console.log(isPlainEmptyObject([]));        // false
JavaScript

業務コードでは、「ここで扱いたいのは“プレーンなオブジェクト”だけか? それとも配列も含めて“中身がない入れ物”全般か?」を最初に決めておくことが大事です。
その設計によって、ユーティリティ関数の中身も変わってきます。


「if (obj)」では空オブジェクトを判定できない理由

初心者がよくやってしまうのが、「if (obj)」でオブジェクトの中身の有無まで判定しようとする書き方です。

const obj = {};

if (obj) {
  console.log("何かあるはず");
} else {
  console.log("何もない");
}
JavaScript

このコードは、obj が空オブジェクトでも「何かあるはず」と判定してしまいます。
なぜなら、JavaScript では「空オブジェクトは truthy(真)」だからです。

console.log(Boolean({})); // true
JavaScript

つまり、「if (obj)」は「null / undefined ではないか」をざっくり見ることはできますが、「プロパティが 1 つもないかどうか」は判定できません。
「空オブジェクトかどうか」をきちんと見たいときは、必ず Object.keys などでプロパティ数を確認する必要があります。


実務での具体的な利用イメージ

検索条件の例で、空オブジェクト判定を使ってみます。

function isPlainEmptyObject(value) {
  if (value === null) return false;
  if (typeof value !== "object") return false;
  if (Array.isArray(value)) return false;
  return Object.keys(value).length === 0;
}

function search(conditions) {
  if (isPlainEmptyObject(conditions)) {
    console.log("条件なしとして全件検索を実行");
  } else {
    console.log("条件付き検索を実行:", conditions);
  }
}

search({});                 // 条件なし
search({ name: "山田" });   // 条件あり
JavaScript

また、PATCH API などで「変更された項目だけを送る」設計にしている場合、
「何も変更されていないならリクエストを送らない」という判定にも使えます。

const patch = {};  // 変更された項目をここに詰めていく

// 何も変更されなかった場合
if (isPlainEmptyObject(patch)) {
  console.log("変更なしのため、API は呼ばない");
} else {
  console.log("変更ありのため、PATCH を送信");
}
JavaScript

このように、「オブジェクトという入れ物はあるが、中身が 0 個かどうか」を見るユーティリティは、業務ロジックの分岐に直結します。


null / undefined と組み合わせた判定

よくある要件に、「null / undefined / 空オブジェクトは全部“設定なし”として扱いたい」というものがあります。
その場合は、nullish 判定と空オブジェクト判定を組み合わせたユーティリティを用意すると便利です。

function isNullish(value) {
  return value == null;  // null または undefined
}

function isEmptyConfig(value) {
  if (isNullish(value)) return true;
  if (typeof value !== "object") return false;
  if (Array.isArray(value)) return false;
  return Object.keys(value).length === 0;
}
JavaScript

使い方のイメージはこんな感じです。

const config = getUserConfig();

if (isEmptyConfig(config)) {
  console.log("ユーザー固有の設定はなく、デフォルト設定を使う");
} else {
  console.log("ユーザー固有の設定を適用する:", config);
}
JavaScript

ここでのポイントは、「業務上“設定なし”とみなす条件を一箇所に集約している」ことです。
画面や機能ごとにバラバラの条件を書いてしまうと、「この画面では空オブジェクトも設定あり扱い」「あっちでは設定なし扱い」など、仕様のブレが発生しやすくなります。


実務でありがちなバグと空オブジェクト

典型的なバグの一つが、「空オブジェクトなのに、何かある前提でプロパティを読んでしまう」ケースです。

const user = {};  // 何らかの理由で空オブジェクトになっている

console.log(user.name.toUpperCase());  // user.name が undefined なのでエラー
JavaScript

user 自体はオブジェクトなのでエラーになりませんが、user.nameundefined になり、その先の .toUpperCase() でエラーになります。
これを避けるには、「そもそも user が空オブジェクトではないか」「name プロパティが存在するか」を事前にチェックする必要があります。

if (!isPlainEmptyObject(user) && typeof user.name === "string") {
  console.log(user.name.toUpperCase());
} else {
  console.log("ユーザー名が設定されていません");
}
JavaScript

あるいは、オプショナルチェイニングやデフォルト値と組み合わせることもできます。

const upperName = (user.name ?? "名無し").toUpperCase();
console.log(upperName);
JavaScript

この場合でも、「user 自体が null / undefined ではないこと」は別途保証しておく必要があります。
「オブジェクトが存在するか」と「中身があるか」は別物だ、という感覚を持てると、実務でのバグがかなり減ります。


小さな練習で感覚をつかむ

理解を定着させるには、自分の手でいろいろな値を試してみるのが一番です。
isEmptyObjectisPlainEmptyObjectisEmptyConfig を自分で実装して、{}, { a: 1 }, [], null, undefined, "abc" などを渡して、結果をコンソールに出してみてください。

そのうえで、「このプロジェクトなら、null / undefined / 空オブジェクトをどう扱うべきか?」を自分なりに言語化してみると、設計の目線が一段上がります。

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