JavaScriptのObject.getOwnPropertyDescriptors():プロパティの設定をまとめて調べる
Object.getOwnPropertyDescriptors()で書き換え可否やgetterの有無を調べる方法を解説。ブラウザーとNode.jsの実行手順、期待出力、非列挙プロパティや参照共有の注意点まで学べます。
オブジェクトに値を代入しても変わらない、一覧に出てこない項目がある、値を読んだだけで処理が走る。こうした現象を調べるには、値だけでなくプロパティの設定を見る必要があります。
Object.getOwnPropertyDescriptors()は、オブジェクト自身が持つプロパティの設定をまとめて取得するAPIです。この記事では、設定オブジェクトを調査する例を通して、書き換え可否とgetterの有無を確認します。
何を取得するAPIなのか
プロパティには、値に加えて「代入できるか」「列挙の対象になるか」といった属性があります。その属性をまとめたオブジェクトをプロパティ記述子と呼びます。
Object.getOwnPropertyDescriptors()は、元のキーに対応する記述子を格納した新しいオブジェクトを返します。引数は調査対象のobjの1つだけで、コールバックやオプションはありません。対象に自身のプロパティがなければ空のオブジェクトを返します。MDNのAPIリファレンス
const item = { price: 1200 };
const descriptors = Object.getOwnPropertyDescriptors(item);
console.log(descriptors.price.value); // 1200
console.log(descriptors.price.writable); // trueメソッド名のOwnは「自身が直接持つ」という意味です。継承したプロパティは対象外ですが、自身の非列挙プロパティとSymbolキーは取得対象です。この範囲はMDNの列挙可能性と所有権の説明で確認できます。
動作環境と実行方法
2026年9月28日時点で確認したMDNでは、このAPIは2017年4月から主要ブラウザーで広く利用可能とされています。現在のChrome、Edge、Firefox、Safariで利用できます。ただし、この年月は古いブラウザーすべての対応を意味しません。古い組み込みブラウザーなどでは、後述する機能検出を行ってください。
Node.jsでも動作します。Node.jsの公式告知では、7.0.0で導入したV8 5.4の機能として、このES2017のAPIが紹介されています。7.0.0は導入時期を示す番号です。これから試す環境にはNode.js 24系など、サポート対象の環境を使います。Node.jsのV8 5.4導入告知
ブラウザーで試す
通常のWebページで開発者ツールを開き、Consoleを選択します。次の節のJavaScriptコード全体を入力して実行してください。ページの要素や外部ライブラリを使わないため、HTMLファイルの準備は不要です。
Node.jsで試す
次の節のコードをUTF-8のinspect-properties.jsとして保存します。そのファイルを保存したフォルダーでターミナルを開き、以下を実行してください。パッケージのインストールやモジュール設定は不要です。
node --version
node inspect-properties.jsこの記事の期待出力は仕様に基づいて点検したもので、実機で実行した結果ではありません。
自己完結したコードで設定を調べる
設定オブジェクトsettingsに、通常の値、読み取り専用の項目、getterを用意します。準備に使うObject.defineProperty()は、プロパティの属性を指定するための補助です。
(() => {
"use strict";
if (typeof Object.getOwnPropertyDescriptors !== "function") {
console.log("この環境は対応していません");
return;
}
let getterCalls = 0;
const settings = {
mode: "preview",
get label() {
getterCalls += 1;
return "mode=" + this.mode;
}
};
Object.defineProperty(settings, "buildId", {
value: "build-a",
writable: false,
enumerable: false,
configurable: false
});
const descriptors = Object.getOwnPropertyDescriptors(settings);
console.log("modeの値: " + descriptors.mode.value);
console.log("modeは代入可能: " + descriptors.mode.writable);
console.log("buildIdは列挙可能: " + descriptors.buildId.enumerable);
console.log("labelはgetter: " + (typeof descriptors.label.get === "function"));
console.log("調査後のgetter実行回数: " + getterCalls);
try {
settings.buildId = "build-b";
} catch (error) {
console.log("代入失敗: " + error.name);
}
descriptors.mode.value = "production";
console.log("記述子変更後のmode: " + settings.mode);
console.log("labelの値: " + settings.label);
console.log("値の取得後のgetter実行回数: " + getterCalls);
})();対応環境での期待出力は次のとおりです。Consoleが最後に表示する評価結果のundefinedは、このログとは別です。
modeの値: preview
modeは代入可能: true
buildIdは列挙可能: false
labelはgetter: true
調査後のgetter実行回数: 0
代入失敗: TypeError
記述子変更後のmode: preview
labelの値: mode=preview
値の取得後のgetter実行回数: 1buildIdは列挙対象外でも、返されたdescriptorsに含まれています。また、調査した時点ではlabelのgetterは実行されず、最後にsettings.labelを読んだときだけ実行されます。
コード全体を即時実行関数で囲んでいるため、Consoleで繰り返し実行しても、前回の変数宣言と衝突しません。
戻り値の記述子を読み解く
値を持つデータプロパティ
modeやbuildIdの記述子には、次の項目があります。
valueは保存されている値です。writableは代入による値の変更が可能かを表します。enumerableは列挙対象になるかを表します。configurableは削除や属性の再設定に関わる設定です。
今回のbuildIdはwritable: falseなので、strictモードでの代入がTypeErrorになります。configurable: falseでも、データプロパティがまだwritable: trueなら値の変更など一部の操作は可能です。「すべて変更禁止」と一括りにしないでください。属性の意味と例外はMDNのプロパティ定義の説明に整理されています。
getterを持つアクセサープロパティ
labelの記述子にはgetとsetがあり、valueとwritableはありません。今回のgetは関数、setはundefinedです。
そのため、descriptors.label.valueを読んでも計算結果は取れません。調査で知りたいのがgetterの存在なら、サンプルのようにgetの型を確認します。計算結果が必要なときに、元のオブジェクトのlabelへアクセスしてください。
失敗しやすい使い方と制約
記述子の書き換えで元の設定は変わらない
取得した記述子は、元のプロパティへの設定画面ではありません。サンプルではdescriptors.mode.valueを変更しても、settings.modeはpreviewのままです。取得した記述子の変更が元の属性に影響しないことは、MDNの個別記述子の説明でも明記されています。
ただし、記述子のvalueがオブジェクトなら、その参照は共有されます。次の例は前のコードと独立して実行できます。
(() => {
const settings = { ui: { theme: "light" } };
const descriptors = Object.getOwnPropertyDescriptors(settings);
descriptors.ui.value.theme = "dark";
console.log(settings.ui.theme); // dark
})();この操作は記述子のvalueを置き換えるのではなく、共有しているオブジェクトの中身を変えています。調査用途では、取得結果から入れ子のデータを書き換えないようにします。
不正な対象と例外を扱う
nullとundefinedを渡すとTypeErrorになります。外部から受け取った値を調査する場合、次のように呼び出しを囲むと、失敗後の処理を続けられます。
(() => {
try {
Object.getOwnPropertyDescriptors(null);
} catch (error) {
console.log("調査失敗: " + error.name);
}
})();期待出力は調査失敗: TypeErrorです。このAPIは同期処理なので、awaitやPromiseの待機は不要です。
また、通常のオブジェクトのgetterを実行しないことと、あらゆる対象で副作用がないことは別です。対象がProxyなら、記述子取得に対応するトラップが動作する可能性があります。外部のオブジェクトを調べる汎用ツールでは、例外が発生し得る処理として扱います。MDNのProxyの記述子取得トラップ
実務での活用事例
設定を更新できない不具合では、まず対象の記述子を取り、データプロパティならwritable、アクセサープロパティならsetを確認すると原因を絞れます。値だけをログに出すより、更新できない理由を説明しやすくなります。
一覧に出ない項目の調査にも使えます。直接アクセスできるのに列挙結果に現れないなら、enumerableを確認します。ただし、非列挙という設定は秘密情報の保護ではありません。この記事の例のように、専用のAPIから取得できます。
開発用の診断表示では、getterを実行せずに「計算される項目が存在する」と表示する用途も考えられます。まず自分で作った通常のオブジェクトを対象にし、取得した記述子を読み取り専用の調査結果として扱うと、導入しやすいでしょう。
公式参考資料の使い分け
APIリファレンスでは引数と戻り値、列挙可能性のガイドでは取得範囲を確認できます。属性を読み解く際はプロパティ定義の説明を、導入時期はNode.jsの公式告知を参照してください。本文中のリンクはいずれも、執筆時に確認した公式資料です。
Googleの優先するニュース提供元にSEの部屋を追加
Googleで、いつも読みたい情報源を選べます。登録可否はGoogleの画面で確認できます。
候補にSEの部屋が表示されない場合は、まだ追加できません。