JavaScriptのObject.getOwnPropertyDescriptorの使い方!プロパティの属性を調べる

JavaScriptのObject.getOwnPropertyDescriptorメソッドは、指定されたオブジェクトの特定の自身のプロパティ(継承されたプロパティではない)のプロパティディスクリプタを取得するために使用されます。

プロパティディスクリプタとは、プロパティのだけでなく、そのプロパティが書き込み可能かwritable)、列挙可能かenumerable)、設定変更可能かconfigurable)、あるいはゲッター/セッターを持つかget/set)といった詳細な属性情報を含むオブジェクトです。
このメソッドは、オブジェクトの内部的な構造を調べたり、Object.definePropertyObject.definePropertiesで設定されたプロパティの挙動を確認したりする際に便利です。

この記事では、Object.getOwnPropertyDescriptorメソッドの基本的な使い方、引数、戻り値、主要な使用例、そして使用する際の重要な注意点について解説します。

Object.getOwnPropertyDescriptorメソッドの基本的な構文

Object.getOwnPropertyDescriptorメソッドの基本的な構文は以下の通りです。

Object.getOwnPropertyDescriptor(obj, propName)
  • obj: 必須。プロパティディスクリプタを取得したいオブジェクトです。
  • propName: 必須。ディスクリプタを取得したいプロパティの名前(文字列またはシンボル)です。

引数1(obj)

  • 必須です。
  • プロパティディスクリプタを取得したいオブジェクトを指定します。
  • null または undefined を渡すと TypeError が発生します。

引数2(propName)

  • 必須です。
  • ディスクリプタを取得したいプロパティの名前を文字列またはシンボルで指定します。

Object.getOwnPropertyDescriptorの戻り値

指定されたプロパティのプロパティディスクリプタオブジェクトが返されます。
プロパティが存在しない場合はundefinedが返されます。

プロパティディスクリプタの確認

Object.getOwnPropertyDescriptorが返すプロパティディスクリプタは、以下の属性のいずれかまたはすべてを含みます。

1. データディスクリプタ (Data Descriptor)

プロパティが直接値を持つ場合。

  • value: プロパティの実際の値。
  • writable: trueの場合、valueを変更できる。
  • enumerable: trueの場合、for...inループやObject.keys()で列挙できる。
  • configurable: trueの場合、プロパティのディスクリプタを変更したり、プロパティを削除したりできる。

2. アクセサディスクリプタ (Accessor Descriptor)

プロパティがゲッター/セッター関数を持つ場合。

  • get: ゲッター関数。プロパティが読み取られたときに呼び出される。
  • set: セッター関数。プロパティが設定されたときに呼び出される。
  • enumerable: true の場合、列挙できる。
  • configurable: true の場合、設定変更・削除できる。

注意

データディスクリプタとアクセサディスクリプタの属性を同時に持つことはありません

Object.getOwnPropertyDescriptorメソッドを使ってみる

実際にObject.getOwnPropertyDescriptorメソッドを使って、プロパティの属性を確認してみます。

例1:通常のデータプロパティのディスクリプタを取得する

通常のプロパティ代入で作成されたプロパティのデフォルト属性を確認します。

const user = {
  name: 'Alice',
  age: 30
};

const nameDescriptor = Object.getOwnPropertyDescriptor(user, 'name');
console.log("nameプロパティのディスクリプタ:", nameDescriptor);
/*
出力:
nameプロパティのディスクリプタ: {
  value: 'Alice',
  writable: true,
  enumerable: true,
  configurable: true
}
*/

const ageDescriptor = Object.getOwnPropertyDescriptor(user, 'age');
console.log("ageプロパティのディスクリプタ:", ageDescriptor);
/*
出力:
ageプロパティのディスクリプタ: {
  value: 30,
  writable: true,
  enumerable: true,
  configurable: true
}
*/

nameageプロパティを持っているuserオブジェクトを作成しました。
その次の処理でObject.getOwnPropertyDescriptorメソッドを使って、nameプロパティのディスクリプタを取得して出力しています。
ageプロパティも同様に取得して出力しました。

確認してみると通常のプロパティは、デフォルトでwritable: true, enumerable: true, configurable: trueであることがわかります。

例2:Object.definePropertyで定義されたプロパティのディスクリプタを取得する

Object.definePropertyで属性を明示的に設定したプロパティのディスクリプタを確認します。

"use strict";

const config = {};
Object.defineProperty(config, 'version', {
  value: '1.0.0',
  writable: false, // 変更不可
  enumerable: false, // 列挙不可
  configurable: false // 削除・再設定不可
});

const versionDescriptor = Object.getOwnPropertyDescriptor(config, 'version');
console.log("versionプロパティのディスクリプタ:", versionDescriptor);
/*
出力:
versionプロパティのディスクリプタ: {
  value: '1.0.0',
  writable: false,
  enumerable: false,
  configurable: false
}
*/

最初にconfigオブジェクトを作成しています。
次の処理でObject.definePropertyを使ってversionプロパティを追加しています。
それぞれの属性をfalseに設定しました。

そして、その次の処理でディスクリプタを取得して出力しました。
versionプロパティの属性が、Object.definePropertyで設定した通りになっていることが確認できます。

例3:ゲッター/セッタープロパティのディスクリプタを取得する

アクセサディスクリプタを持つプロパティの情報を確認します。

const circle = {
  _radius: 0
};
Object.defineProperty(circle, 'radius', {
  get: function() { return this._radius; },
  set: function(newRadius) { this._radius = newRadius; },
  enumerable: true,
  configurable: true
});

const radiusDescriptor = Object.getOwnPropertyDescriptor(circle, 'radius');
console.log("radiusプロパティのディスクリプタ:", radiusDescriptor);
/*
出力:
radiusプロパティのディスクリプタ: {
  get: [Function: get], // ゲッター関数
  set: [Function: set], // セッター関数
  enumerable: true,
  configurable: true
}
*/

circleオブジェクトを作成して、次の処理でradiusプロパティを追加しました。
radiusプロパティには、getsetを持っているプロパティです。

次の処理でObject.getOwnPropertyDescriptorを使用して、radiusプロパティを取得しています。
出力して確認するとradiusプロパティがvaluewritableではなく、getsetを持つアクセサディスクリプタであることがわかります。

例4:存在しないプロパティや継承されたプロパティの場合

Object.getOwnPropertyDescriptorは、存在しないプロパティや継承されたプロパティに対しては undefined を返します。

const animal = {
  eats: true
};
const rabbit = Object.create(animal);
rabbit.jumps = true;

// 存在しないプロパティ
const nonExistentDescriptor = Object.getOwnPropertyDescriptor(rabbit, 'color');
console.log("存在しないプロパティのディスクリプタ:", nonExistentDescriptor); // 出力: undefined

// 継承されたプロパティ
console.log(rabbit.eats); // true
const inheritedDescriptor = Object.getOwnPropertyDescriptor(rabbit, 'eats');
console.log("継承されたプロパティのディスクリプタ:", inheritedDescriptor); // 出力: undefined
// eats は rabbit のプロトタイプである animal に存在するため、undefined が返される。
// animal の eats プロパティのディスクリプタを取得するには、Object.getOwnPropertyDescriptor(animal, 'eats') を使う必要がある。

最初にanimalオブジェクトを作成しています。
そのあとにanimalオブジェクトをプロトタイプとして使用して、rabbitオブジェクトを作成しました。

rabbitcolorプロパティにアクセスしようとしていますが、存在しないためundefinedが返ります。

次にeatsプロパティは存在していますが、これは継承されたプロパティです。
そのためrabbit自身のプロパティとしては存在せずundefinedが返されます。

Object.getOwnPropertyDescriptorを使う際の注意点

Object.getOwnPropertyDescriptorを使うときの注意点です。

自身のプロパティのみを対象とする

最も重要な注意点です。
プロトタイプチェーンを通じてアクセスできる継承されたプロパティのディスクリプタは取得できません。
あくまでオブジェクト自身が直接持つプロパティのみが対象です。

存在しない場合はundefinedを返す

プロパティが存在しない場合、エラーではなく undefined が返されます。
この戻り値を適切にチェックする必要があります。

nullまたはundefinedのオブジェクト引数

Object.getOwnPropertyDescriptor(null, 'prop')Object.getOwnPropertyDescriptor(undefined, 'prop')を呼び出すとTypeErrorが発生します。

Object.getOwnPropertyDescriptors

  • Object.getOwnPropertyDescriptor(obj, propName): 単一の指定された自身のプロパティのディスクリプタを返します。
  • Object.getOwnPropertyDescriptors(obj): オブジェクトのすべての自身のプロパティのディスクリプタを含む新しいオブジェクトを返します。複数のプロパティの情報を一度に取得したい場合に便利です。
const myObj = { a: 1, b: 2 };
console.log(Object.getOwnPropertyDescriptor(myObj, 'a')); // { value: 1, ... }
console.log(Object.getOwnPropertyDescriptors(myObj)); // { a: { value: 1, ... }, b: { value: 2, ... } }

まとめ

JavaScriptのObject.getOwnPropertyDescriptorメソッドは、オブジェクトの特定の自身のプロパティに関する詳細な属性情報(プロパティディスクリプタ)を取得するための強力なツールです。
プロパティが書き込み可能か、列挙可能か、ゲッター/セッターを持つかなどをプログラムで確認したい場合に便利です。

自身のプロパティのみを対象とすること、存在しない場合は undefinedを返すこと、そしてObject.getOwnPropertyDescriptorsとの使い分けを理解して使用することが不可欠です。
これらのポイントを踏まえ、Object.getOwnPropertyDescriptorを効果的に活用し、JavaScriptアプリケーションの堅牢性と柔軟性を高めましょう。

コメント