JavaScriptリフレクション

概要:このチュートリアルでは、ES6におけるJavaScriptのリフレクションとReflect APIについて学びます。

リフレクションとは

コンピュータプログラミングにおいて、リフレクションとは、プログラムが実行時にオブジェクトの変数、プロパティ、およびメソッドを操作する機能です。

ES6以前でも、JavaScriptにはリフレクション機能が既に存在していましたが、コミュニティや仕様書では正式にそう呼ばれていませんでした。たとえば、`Object.keys()`、`Object.getOwnPropertyDescriptor()`、`Array.isArray()`などのメソッドは、従来のリフレクション機能です。

ES6では、メソッドの呼び出し、オブジェクトの構築、プロパティの取得と設定、プロパティの操作と拡張を可能にする`Reflect`という新しいグローバルオブジェクトが導入されました。

`Reflect` APIは、動的コードを処理できるプログラムやフレームワークの開発を可能にするため、重要です。

Reflect API

ほとんどのグローバルオブジェクトとは異なり、`Reflect`はコンストラクタではありません。つまり、`new`演算子で`Reflect`を使用したり、`Reflect`を関数として呼び出したりすることはできません。 `Math`オブジェクトや`JSON`オブジェクトと似ています。 `Reflect`オブジェクトのすべてのメソッドは静的です。

  • `Reflect.apply()` – 指定された引数で関数を呼び出します。
  • `Reflect.construct()` – `new`演算子のように動作しますが、関数として動作します。 `new target(...args)`を呼び出すことと同じです。
  • `Reflect.defineProperty()` – `Object.defineProperty()`に似ていますが、プロパティがオブジェクトに正常に定義されたかどうかを示すブール値を返します。
  • `Reflect.deleteProperty()` – `delete`演算子のように動作しますが、関数として動作します。 `delete objectName[propertyName]`を呼び出すことと同じです。
  • `Reflect.get()` – プロパティの値を返します。
  • `Reflect.getOwnPropertyDescriptor()` – `Object.getOwnPropertyDescriptor()`に似ています。オブジェクトにプロパティが存在する場合は、そのプロパティのプロパティ記述子を返し、そうでない場合は`undefined`を返します。
  • `Reflect.getPrototypeOf()` – `Object.getPrototypeOf()`と同じです。
  • `Reflect.has()` – `in`演算子のように動作しますが、関数として動作します。プロパティ(所有または継承)が存在するかどうかを示すブール値を返します。
    `Reflect.isExtensible()` – `Object.isExtensible()`と同じです。
  • `Reflect.ownKeys()` – オブジェクトの所有プロパティキー(継承されていない)の配列を返します。
  • `Reflect.preventExtensions()` – `Object.preventExtensions()`に似ています。ブール値を返します。
  • `Reflect.set()` – プロパティに値を代入し、プロパティが正常に設定された場合はtrueとなるブール値を返します。
  • `Reflect.setPrototypeOf()` – オブジェクトのプロトタイプを設定します。

Reflect APIの使用例をいくつか見てみましょう。

オブジェクトの作成:Reflect.construct()

`Reflect.construct()`メソッドは、`new`演算子のように動作しますが、関数として動作します。異なるプロトタイプを指定できることを除けば、`new target(...args)`を呼び出すことと同じです。

Reflect.construct(target, args [, newTarget])
Code language: CSS (css)

`Reflect.construct()`は、`target`の新しいインスタンス、または指定された場合は`newTarget`を返します。これは、`target`によってコンストラクタとして初期化され、指定された配列のようなオブジェクト`args`が使用されます。次の例を参照してください。

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
    get fullName() {
        return `${this.firstName} ${this.lastName}`;
    }
};

let args = ['John', 'Doe'];

let john = Reflect.construct(
    Person,
    args
);

console.log(john instanceof Person);
console.log(john.fullName); // John Doe
Code language: JavaScript (javascript)

出力

true
John Doe
Code language: JavaScript (javascript)

この例では

  • まず、`Person`というクラスを定義します。
  • 次に、2つの文字列を含む`args`配列を宣言します。
  • 3番目に、`Reflect.construct()`メソッドを使用して`Person`クラスの新しいインスタンスを作成します。 `john`オブジェクトは`Person`クラスのインスタンスであるため、`fullName`プロパティを持っています。

関数の呼び出し:Reflect.apply()

ES6以前は、`Function.prototype.apply()`メソッドを使用して、指定された`this`値と`arguments`で関数を呼び出していました。例えば

let result = Function.prototype.apply.call(Math.max, Math, [10, 20, 30]);
console.log(result);
Code language: JavaScript (javascript)

出力

30

この構文はかなり冗長です。

`Reflect.apply()`は、`Function.prototype.apply()`と同じ機能を提供しますが、冗長性が少なく、理解しやすいです。

let result = Reflect.apply(Math.max, Math, [10, 20, 30]);
console.log(result);
Code language: JavaScript (javascript)

`Reflect.apply()`メソッドの構文は次のとおりです。

Reflect.apply(target, thisArg, args)
Code language: JavaScript (javascript)

プロパティの定義:Reflect.defineProperty()

`Reflect.defineProperty()`は`Object.defineProperty()`に似ています。ただし、例外をスローする代わりに、プロパティが正常に定義されたかどうかを示すブール値を返します。

Reflect.defineProperty(target, propertyName, propertyDescriptor)
Code language: JavaScript (javascript)

次の例を参照してください。

let person = {
    name: 'John Doe'
};

if (Reflect.defineProperty(person, 'age', {
        writable: true,
        configurable: true,
        enumerable: false,
        value: 25,
    })) {
    console.log(person.age);
} else {
    console.log('Cannot define the age property on the person object.');

}Code language: JavaScript (javascript)

このチュートリアルでは、JavaScriptのリフレクションと、多数のリフレクションメソッドを含むReflect APIについて学びました。

このチュートリアルは役に立ちましたか?