JavaScript

JavaScriptのReflect.ownKeys():非列挙プロパティとSymbolを含むキー一覧を取得する

この記事でわかること

Reflect.ownKeys()でオブジェクト自身のキーを漏れなく取得する方法を解説。非列挙プロパティとSymbol、キーの順序、ブラウザーとNode.jsの実行手順、例外や用途の制約を学べます。

目的:普段の一覧に出てこないキーを調べる

オブジェクトの項目を調べたとき、画面上では使えるプロパティなのに、キー一覧には見つからないことがあります。原因として、非列挙プロパティやSymbolをキーに持つプロパティが考えられます。

Reflect.ownKeys()は、オブジェクト自身が持つ文字列キーとSymbolキーを、列挙可能かどうかに関係なく取得するAPIです。ライブラリーが返すオブジェクトの調査や、設定データの意図しない項目を見つける場面に向いています。

この記事では、プロパティの値や設定をまとめて取得するのではなく、「どのキーが存在するか」の確認に絞ります。目的が通常の表示用データの処理なら、対象範囲が広いことを理解してから採用しましょう。

動作環境と対応状況

2026年11月5日時点で、MDNはReflect.ownKeys()を広く利用可能な機能として扱い、2016年9月以降、主要ブラウザー間で利用できると説明しています。現在のChrome、Edge、Firefox、Safariで利用できます。詳細はMDNのReflect.ownKeys()リファレンスを参照してください。

ブラウザー専用のAPIではないため、Node.jsでも動作します。この記事の実行環境はNode.js 22以降とします。これは例を試す対象バージョンであり、このAPIの初搭載バージョンを意味しません。追加パッケージやDOM、ネットワーク接続は不要です。

ブラウザーでの実行方法

通常のWebページを開き、開発者ツールのConsoleで、後述するJavaScriptコード全体を実行します。コードは即時実行関数で囲んでいるため、繰り返し実行してもトップレベルの変数宣言が衝突しません。Consoleが貼り付けを制限する場合は、内容を確認したうえでブラウザーの案内に従ってください。

Node.jsでの実行方法

後述するコードをUTF-8のown-keys-demo.cjsとして保存します。ターミナルで保存先のフォルダーを開き、次のコマンドを実行します。

Shell
node --version
node own-keys-demo.cjs

.cjsを使うため、周囲のpackage.jsonのモジュール設定に依存せず試せます。ファイルを指定して起動する方法はNode.js公式のスクリプト実行ガイドに沿っています。

引数・戻り値と対象範囲

呼び出しの形は次のとおりです。

JavaScript
Reflect.ownKeys(target)
  • 引数targetは調べたいオブジェクトです。配列や関数も渡せます。
  • 戻り値は、キーを要素に持つ新しい配列です。各要素は文字列またはSymbolです。
  • オブジェクト自身のプロパティが対象です。プロトタイプから継承したプロパティは含みません。
  • 非列挙プロパティも取得します。キーがなければ空配列を返します。
  • null、undefined、文字列や数値などのプリミティブ値を渡すとTypeErrorになります。

列挙可能とは、プロパティのenumerableという設定がtrueであることです。通常の代入で追加したプロパティは列挙可能ですが、Object.defineProperty()では明示しない限り非列挙になります。

Object.keys()が取得するのは、自身が持つ列挙可能な文字列キーです。これに対し、今回のAPIは非列挙キーとSymbolキーも取得します。この対象範囲の違いはMDNの列挙可能性と所有権の解説で確認できます。

自己完結したコードで取得範囲を確かめる

次のコードは、継承したプロパティ、数値に見えるキー、非列挙プロパティ、Symbolキーを持つオブジェクトを作ります。出力は配列の表示形式に左右されないよう、明示的に文字列へ変換しています。

JavaScript
(() => {
  const internalKey = Symbol("internal");
  const prototype = { inherited: "親の項目" };
  const settings = Object.create(prototype);

  settings.title = "SEの部屋";
  settings[10] = "十番";
  settings[2] = "二番";

  Object.defineProperty(settings, "hidden", {
    value: "補助情報",
    enumerable: false,
  });

  settings[internalKey] = "内部識別子";

  const keys = Reflect.ownKeys(settings);
  const labels = keys.map((key) => String(key));

  console.log("全キー: " + labels.join(" | "));
  console.log("通常の一覧: " + Object.keys(settings).join(" | "));
  console.log("継承項目を含む: " + keys.includes("inherited"));
  console.log("Symbolの同一性: " + keys.includes(internalKey));
  console.log("非列挙の値: " + settings.hidden);

  keys.pop();
  console.log(
    "配列変更後もSymbolは存在: " +
      Reflect.ownKeys(settings).includes(internalKey)
  );
})();

仕様から導ける期待出力は次のとおりです。ここではコードを実行しておらず、実機検証結果ではありません。

CODE
全キー: 2 | 10 | title | hidden | Symbol(internal)
通常の一覧: 2 | 10 | title
継承項目を含む: false
Symbolの同一性: true
非列挙の値: 補助情報
配列変更後もSymbolは存在: true

hiddenは通常の一覧から除外されますが、存在しているため値を読み出せます。非列挙という設定は、アクセスを禁止するものではありません。

また、labelsへの変換は表示のためだけです。実際のkeys配列には元のSymbolが入るため、internalKeyとの同一性を確かめられます。戻り値の配列を変更しても、元のオブジェクトからプロパティが削除されることはありません。

キーの順序を読み違えない

通常のオブジェクトでは種類ごとに並ぶ

通常のオブジェクトでは、配列インデックスに当たる文字列キーが数値の昇順で先に並び、その他の文字列キー、Symbolキーがそれぞれ作成順で続きます。

例では10を先に追加しましたが、出力は2、10の順です。これらも戻り値では数値ではなく文字列です。また、"01"や"-1"のようなキーは、この例の"2"と同じインデックス扱いにはなりません。

入力した順序をそのまま記録する用途や、表示順を業務上のルールとして管理する用途では、取得結果の順序に頼らず、別途順序を定義してください。

配列には要素以外のキーもある

配列もオブジェクトなので渡せます。ただし、取得結果は要素の添字だけとは限りません。

JavaScript
(() => {
  const items = ["A", "B"];
  console.log(Reflect.ownKeys(items).join(" | "));
})();

期待出力は次のとおりです。

CODE
0 | 1 | length

lengthは配列自身の非列挙プロパティです。したがって、キー配列の長さを配列の要素数として使うと誤りになります。

失敗例と制約

プリミティブ値は例外になる

文字列を渡して文字位置の一覧を得ようとすると失敗します。外部から受け取った値を調べる場合は、例外の扱いを決めておきます。

JavaScript
(() => {
  try {
    Reflect.ownKeys("abc");
  } catch (error) {
    console.log(error instanceof TypeError);
  }
})();

期待出力はtrueです。エラーメッセージ本文は実行環境で異なるため、比較対象にしていません。APIは同期的に配列を返すので、awaitやPromiseの拒否処理は不要です。

Symbolを文字列化すると区別が失われる

Symbol("internal")を別々に作ると、説明が同じでも異なるキーになります。どちらも表示用に変換するとSymbol(internal)になるため、その表示文字列をキーの識別や照合に使わないでください。照合には元のSymbolを保持します。

Proxyでは処理の介入や例外があり得る

通常のオブジェクトではキー取得だけでプロパティのgetterを呼び出しません。ただし、対象がProxyなら、キー一覧を取得する操作に独自処理が介入できます。順序が通常と異なる場合や、独自処理から例外が投げられる場合があります。

そのため、任意のオブジェクトを受け取る調査ツールでは、取得処理そのものをtry...catchで囲む設計が有効です。Proxyの制約違反でもTypeErrorが発生し得ます。詳細はMDNのProxyのownKeysハンドラーに記載されています。

活用事例と採用の判断

ライブラリー更新後にオブジェクトの構造が変わった疑いがある場合、このAPIで自身のキーを列挙すると、通常の一覧では見えない項目の存在を調べられます。値をすべて読み出さず、まずキーだけ確認する調査にも向いています。

設定オブジェクトの検査では、文字列キーとSymbolキーの両方を許可リストと照合できます。ただし、キーの存在だけでは値の型や妥当性までは保証できません。キーの検査と値の検査を別々に設計する必要があります。

実務での改善案は、最初に「表示用の項目を取りたいのか、構造を調べたいのか」を決めることです。表示用ならObject.keys()で十分な場合があります。非列挙プロパティやSymbolも含めた調査が必要なときにReflect.ownKeys()を選ぶと、取得範囲と目的が一致します。

公式参考資料

PREFERRED SOURCES

Googleの優先するニュース提供元にSEの部屋を追加

Googleで、いつも読みたい情報源を選べます。登録可否はGoogleの画面で確認できます。

Googleの設定画面で確認する 新しいタブで開きます

候補にSEの部屋が表示されない場合は、まだ追加できません。

y.
WRITTEN BY

y_ymo10

SEの部屋で、HTML・CSSからJavaScript・React・Next.jsまで、Web制作の開発ノートを公開しています。

ほかの開発ノートを読む →