Reflectメソッドとは?

Reflect APIは、JavaScriptでオブジェクトや関数に対する操作を行うための組み込みAPIです。プロパティの取得・設定・削除、プロパティの存在確認、関数の呼び出し、インスタンスの生成などを、Reflectオブジェクトの静的メソッドとして実行できます。
例えば、通常はuser.nameやuser["name"]で行うプロパティの取得を、Reflect.get()で記述できます。また、delete演算子による削除は、Reflect.deleteProperty()で行えます。
単純なオブジェクト操作では従来の書き方でも問題ありませんが、処理結果を戻り値で確認したい場合や、Proxyと組み合わせる場合にReflect APIが役立ちます。
Reflectメソッドの利点
Reflect APIには、次のような利点があります。
- オブジェクト操作を統一的に記述できる:取得・設定・削除・存在確認などを
Reflectのメソッドとして記述できます。 - 操作結果を確認しやすい:
Reflect.set()やReflect.deleteProperty()は、成功したかどうかをブール値で返します。 - Proxyと組み合わせやすい:多くのReflectメソッドは、Proxyのトラップと対応する名前・引数を持っています。
- 関数の呼び出しやインスタンス生成も扱える:
Reflect.apply()やReflect.construct()を利用できます。
なお、Reflect APIを使用しても、すべての例外を防げるわけではありません。不正な引数を渡した場合などには、TypeErrorが発生することがあります。
Reflect.getの使用例
Reflect.get()は、オブジェクトから指定したプロパティの値を取得するメソッドです。
const user = {
name: "John",
age: 25
};
const name = Reflect.get(user, "name");
console.log(name); // John
基本構文は次のとおりです。
Reflect.get(対象オブジェクト, プロパティ名);
通常のuser.nameやuser["name"]と同様に、プロパティの値を取得できます。
指定したプロパティが存在しない場合は、undefinedが返されます。
const user = {
name: "John"
};
console.log(Reflect.get(user, "address")); // undefined
また、第1引数にはオブジェクトを指定する必要があります。nullやundefinedなどを指定するとTypeErrorが発生します。
Reflect.setの使用例
Reflect.set()は、オブジェクトのプロパティへ値を設定するメソッドです。
const user = {
name: "John",
age: 25
};
const result = Reflect.set(user, "age", 26);
console.log(result); // true
console.log(user.age); // 26
Reflect.set()は、設定に成功した場合はtrue、設定できなかった場合はfalseを返します。
戻り値を確認することで、代入処理が成功したかどうかを明示的に判定できます。
const settings = {};
if (Reflect.set(settings, "theme", "dark")) {
console.log("設定に成功しました");
}
console.log(settings.theme); // dark
例えば、書き込み禁止(writable: false)のプロパティへ代入しようとした場合は、falseが返されます。
const user = {};
Object.defineProperty(user, "id", {
value: 1001,
writable: false
});
const result = Reflect.set(user, "id", 2000);
console.log(result); // false
console.log(user.id); // 1001
Reflect.deletePropertyの使用例
Reflect.deleteProperty()は、オブジェクトから指定したプロパティを削除するメソッドです。
const user = {
name: "John",
age: 25
};
const result = Reflect.deleteProperty(user, "age");
console.log(result); // true
console.log(user); // { name: "John" }
削除に成功した場合はtrue、削除できなかった場合はfalseが返されます。
通常のdelete user.ageと似た操作ですが、Reflect APIでは削除操作をメソッドとして統一的に記述できます。
configurable: falseのプロパティは削除できません。
const user = {};
Object.defineProperty(user, "id", {
value: 1001,
configurable: false
});
console.log(
Reflect.deleteProperty(user, "id")
); // false
Reflect.hasの使用例
Reflect.has()は、指定したプロパティがオブジェクトに存在するかどうかを確認するメソッドです。
const user = {
name: "John",
age: 25
};
console.log(Reflect.has(user, "name")); // true
console.log(Reflect.has(user, "address")); // false
Reflect.has()は、in演算子と同じように、オブジェクト自身だけでなく、プロトタイプチェーン上のプロパティも確認します。
const user = {
name: "John"
};
console.log(
Reflect.has(user, "toString")
); // true
オブジェクト自身が持つプロパティだけを調べたい場合は、Object.hasOwn()を使用します。
const user = {
name: "John"
};
console.log(Object.hasOwn(user, "name")); // true
console.log(Object.hasOwn(user, "toString")); // false
Reflect.ownKeysの使用例
Reflect.ownKeys()は、オブジェクト自身が持つすべてのプロパティキーを配列として取得するメソッドです。
文字列のプロパティ名だけでなく、列挙不可能なプロパティやSymbolキーも取得できます。
const id = Symbol("id");
const user = {
name: "John",
[id]: 1001
};
Object.defineProperty(user, "age", {
value: 25,
enumerable: false
});
const keys = Reflect.ownKeys(user);
console.log(keys);
// ["name", "age", Symbol(id)]
Object.keys()は列挙可能な文字列キーだけを取得しますが、Reflect.ownKeys()は列挙不可能なキーやSymbolキーも取得できます。
console.log(Object.keys(user));
// ["name"]
console.log(Reflect.ownKeys(user));
// ["name", "age", Symbol(id)]
Reflect.applyの使用例
Reflect.apply()は、指定した関数を呼び出すメソッドです。関数内でthisとして使用する値と、引数の配列を指定します。
function greet(greeting, punctuation) {
return `${greeting}, ${this.name}${punctuation}`;
}
const user = {
name: "John"
};
const message = Reflect.apply(
greet,
user,
["Hello", "!"]
);
console.log(message); // Hello, John!
これは、関数のapply()メソッドと似た処理です。
const message = greet.apply(
user,
["Hello", "!"]
);
通常は関数自身が持つapply()でも十分ですが、Reflect.apply()を使うことで、関数呼び出しもReflect APIとして統一的に扱えます。
Reflect.constructの使用例
Reflect.construct()は、コンストラクタ関数やクラスから新しいインスタンスを生成するメソッドです。newキーワードとほぼ同じ働きをします。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const john = Reflect.construct(
Person,
["John", 25]
);
console.log(john);
// Person { name: "John", age: 25 }
第1引数にはコンストラクタ、第2引数にはコンストラクタへ渡す引数の配列を指定します。
単純なインスタンス生成であれば、通常のnewの方が読みやすいでしょう。
const john = new Person(
"John",
25
);
Reflect.construct()は、使用するコンストラクタを実行時に切り替えたり、引数を配列として扱いたい場合に便利です。
Reflect.definePropertyの使用例
Reflect.defineProperty()は、オブジェクトへプロパティを定義するメソッドです。値だけでなく、書き換えや列挙の可否なども設定できます。
const user = {};
const result = Reflect.defineProperty(
user,
"name",
{
value: "John",
writable: false,
enumerable: true,
configurable: false
}
);
console.log(result); // true
console.log(user.name); // John
Object.defineProperty()は対象オブジェクトを返しますが、Reflect.defineProperty()は成功したかどうかをtrueまたはfalseで返します。
Reflectメソッドの一覧
| メソッド | 主な用途 | 対応する操作 |
|---|---|---|
Reflect.get() |
プロパティの値を取得する | object[key] |
Reflect.set() |
プロパティへ値を設定する | object[key] = value |
Reflect.has() |
プロパティの存在を確認する | key in object |
Reflect.deleteProperty() |
プロパティを削除する | delete object[key] |
Reflect.ownKeys() |
自身が持つすべてのキーを取得する | Object.getOwnPropertyNames()など |
Reflect.apply() |
関数を指定したthisと引数で呼び出す | Function.prototype.apply() |
Reflect.construct() |
新しいインスタンスを生成する | new |
Reflect.defineProperty() |
プロパティを定義する | Object.defineProperty() |
Reflect.getPrototypeOf() |
プロトタイプを取得する | Object.getPrototypeOf() |
Reflect.setPrototypeOf() |
プロトタイプを設定する | Object.setPrototypeOf() |
Reflect.isExtensible() |
プロパティ追加が可能か確認する | Object.isExtensible() |
Reflect.preventExtensions() |
プロパティ追加を禁止する | Object.preventExtensions() |
Reflect.getOwnPropertyDescriptor() |
プロパティ記述子を取得する | Object.getOwnPropertyDescriptor() |
ProxyとReflectを組み合わせる
Reflect APIは、Proxyと組み合わせる場面で特に役立ちます。Proxyのトラップ内からReflectメソッドを呼び出すことで、本来のオブジェクト操作を維持しながら、追加処理を実行できます。
const user = {
name: "John",
age: 25
};
const proxy = new Proxy(user, {
get(target, property, receiver) {
console.log(`${String(property)}を取得しました`);
return Reflect.get(
target,
property,
receiver
);
},
set(target, property, value, receiver) {
console.log(
`${String(property)}を${value}に変更します`
);
return Reflect.set(
target,
property,
value,
receiver
);
}
});
console.log(proxy.name);
proxy.age = 26;
この例では、Proxyのgetトラップとsetトラップを利用しています。
- プロパティを取得すると、取得したプロパティ名を表示します。
- その後、
Reflect.get()によって本来のプロパティ取得処理を実行します。 - プロパティへ値を設定すると、変更内容を表示します。
- 最後に
Reflect.set()で実際の代入処理を行います。
このように、Proxyでは独自の処理を追加しながら、実際のオブジェクト操作はReflect APIへ任せるのが一般的です。
また、receiverをそのままReflect.get()やReflect.set()へ渡すことで、アクセサープロパティ(getter・setter)を利用している場合でも、本来の動作を維持できます。
Reflect APIを使う際の注意点
- 単純なプロパティ取得や設定では、
object.propertyやobject[key]を使用した方が読みやすい場合があります。 - Reflect APIを使用しても、すべての例外を防げるわけではありません。
- 多くのReflectメソッドでは、第1引数にオブジェクトを指定する必要があります。
Reflect.has()は、プロトタイプチェーン上のプロパティも確認します。- オブジェクト自身が持つプロパティだけを確認したい場合は、
Object.hasOwn()を使用します。 Reflect.set()やReflect.deleteProperty()は、戻り値を確認することで操作の成功・失敗を判断できます。- Proxyのトラップ内では、対応するReflectメソッドを利用すると、本来の動作を維持しやすくなります。
まとめ
- Reflect APIは、オブジェクトや関数に対する操作を統一的に行うための組み込みAPIである。
Reflect.get()はプロパティを取得し、Reflect.set()は値を設定する。Reflect.deleteProperty()は、プロパティの削除結果をブール値で返す。Reflect.has()は、in演算子と同様にプロパティの存在を確認する。Reflect.ownKeys()を使用すると、文字列キーだけでなく列挙不可能なキーやSymbolキーも取得できる。Reflect.apply()は関数を呼び出し、Reflect.construct()はインスタンスを生成する。Reflect.defineProperty()は、プロパティを定義し、成功・失敗をブール値で返す。- Reflect APIは、特にProxyと組み合わせる場合に効果を発揮する。
- Reflect APIを使用しても、すべての例外を防げるわけではないため、不正な引数には注意する。