Reflect とは何か(まずイメージから)
Reflect は ES6 で追加された、
「オブジェクトに対する基本操作を、“関数の形”でまとめて持っている道具箱」 です。
これまでバラバラに存在していた
- プロパティの取得 / 設定
- プロパティの削除
in演算子による存在確認Object.definePropertyなどのメタ操作
といったものを、Reflect.get, Reflect.set, Reflect.deleteProperty …のように
一つのオブジェクトに整理して置いてくれた、という位置づけです。
そして、Proxy と組み合わせるときにほぼ必ず登場する相棒でもあります。
ここが重要です。
Reflect は「新しい魔法」ではなく、
「今まであったことを “きれいに・一貫した形で” やり直した API 集合」 だと思うとスッと入ってきます。
従来の書き方 vs Reflect の書き方
まずは、「何が変わるの?」をコードでざっくり見てみます。
プロパティの読み書き
今まで:
const user = { name: "Alice" };
// 読み取り
console.log(user.name);
// 書き込み
user.name = "Bob";
JavaScriptReflect を使うと:
const user = { name: "Alice" };
// 読み取り
console.log(Reflect.get(user, "name")); // "Alice"
// 書き込み
Reflect.set(user, "name", "Bob");
console.log(user.name); // "Bob"
JavaScript見た目は少し遠回りですが、
「操作を全部関数として扱える」という意味で、Reflect 版にも価値があります。
プロパティの削除
今まで:
delete user.name;
JavaScriptReflect:
Reflect.deleteProperty(user, "name");
JavaScriptプロパティの存在確認(in の代わり)
今まで:
console.log("name" in user); // true / false
JavaScriptReflect:
console.log(Reflect.has(user, "name"));
JavaScriptプロパティ定義(Object.defineProperty の代わり)
今まで:
Object.defineProperty(user, "age", {
value: 20,
writable: true,
enumerable: true,
configurable: true,
});
JavaScriptReflect:
Reflect.defineProperty(user, "age", {
value: 20,
writable: true,
enumerable: true,
configurable: true,
});
JavaScriptこのあたりは見た目はほぼ同じですが、
「成功 / 失敗の返り値」などの挙動が整理されています(後で少し触れます)。
ここが重要です。
Reflect は「できること」を増やすというより、
「既にある操作を、統一された関数 API として提供し直した」 というイメージです。
なぜ Reflect がわざわざ作られたのか
1. 「関数として呼びたい」場面があるから
例えば、Proxy の trap(get, set など)から「本来の動作」を呼びたいとき、target[prop] と書くこともできますが、
毎回「this や受け取り方」を気にするのは面倒です。
Reflect を使えば、「オブジェクトへの基本操作」が全部関数なので、
そのまま呼び出せます。
const handler = {
get(target, prop, receiver) {
console.log("読み取り:", prop);
return Reflect.get(target, prop, receiver); // 素の挙動に“委譲”
},
};
JavaScriptProxy で横取りするけど、一部は標準の動きに任せたい、
というときに Reflect がとても自然に使えます。
2. 戻り値の仕様をそろえたかった
例えば Object.defineProperty。
失敗したときの挙動が少しややこしく、
- 成功 → オブジェクトを返す
- 失敗 → 例外を投げる
という形でした。
Reflect 版の Reflect.defineProperty は、
成功 / 失敗を true / false の返り値で返します。
const obj = {};
const ok = Reflect.defineProperty(obj, "x", { value: 1 });
console.log(ok); // true(成功)
// 失敗するケースでは false が返る(例外ではなく)
JavaScript「例外」を使いたくない場面や、
「とりあえず試してダメだったら別の処理へ」というロジックを組みたいときに、true / false のほうが扱いやすいことがあります。
同じことが Reflect.set や Reflect.deleteProperty にも当てはまります。
成功したかどうかが boolean で返ってきます。
3. 「メタプログラミング用の場所」としてまとめたかった
これまで、メタっぽい操作はあちこちに散らばっていました。
Object.defineProperty,Object.getOwnPropertyDescriptorin演算子delete演算子Function.prototype.applyなど
それを「メタプロっぽい操作は Reflect に集約しよう」という整理の意味もあります。
ここが重要です。
Reflect は、Proxy とセットで使うための「素の挙動を表す標準 API」 としての役割がとても大きいです。
Proxy の trap の中で Reflect.xxx を呼ぶ、という形が非常に多く登場します。
よく使う Reflect メソッドと具体例
初心者のうちに「これだけ知っておくといい」というものに絞って説明します。
Reflect.get:プロパティの取得(通常の obj[prop] の関数版)
const user = { name: "Alice", age: 20 };
console.log(Reflect.get(user, "name")); // "Alice"
console.log(Reflect.get(user, "age")); // 20
JavaScript第 3 引数に receiver を渡すと、
ゲッター(getter)がある場合の this をコントロールできますが、
最初のうちは意識しなくても大丈夫です。
Reflect.set:プロパティの設定(obj[prop] = value の関数版)
const user = { name: "Alice" };
const ok = Reflect.set(user, "name", "Bob");
console.log(ok); // true(成功したかどうか)
console.log(user.name); // "Bob"
JavaScriptReflect.has:in 演算子の関数版
const user = { name: "Alice" };
console.log(Reflect.has(user, "name")); // true
console.log(Reflect.has(user, "age")); // false
console.log("name" in user); // 同じ結果
JavaScriptReflect.deleteProperty:delete obj[prop] の関数版
const user = { name: "Alice", age: 20 };
const deleted = Reflect.deleteProperty(user, "age");
console.log(deleted); // true
console.log(user.age); // undefined
JavaScriptReflect.ownKeys:すべての自プロパティキーを取得
Object.keys は列挙可能な文字列キーだけを返しますが、Reflect.ownKeys は Symbol を含むすべての自プロパティキーを返します。
const sym = Symbol("id");
const obj = {
a: 1,
[sym]: 2,
};
console.log(Object.keys(obj)); // ["a"]
console.log(Reflect.ownKeys(obj)); // ["a", Symbol(id)]
JavaScriptReflect.apply:関数を指定した this / 引数で呼び出す
func.apply(thisArg, argsArray) の Reflect 版です。
function sum(a, b) {
return a + b;
}
const result = Reflect.apply(sum, null, [1, 2]);
console.log(result); // 3
JavaScriptProxy の apply trap 内で、「元の関数をそのまま呼びたい」場合などに使います。
Proxy と Reflect の組み合わせ(ここが一番おいしい)
Reflect が一気に「必要なやつ」に見えてくるのは、Proxy と合わせて使ったときです。
例:get を横取りしてログを出しつつ、本来の挙動は保つ
const user = { name: "Alice", age: 20 };
const handler = {
get(target, prop, receiver) {
console.log("アクセス:", prop);
return Reflect.get(target, prop, receiver); // 本来の get を呼ぶ
},
};
const proxy = new Proxy(user, handler);
console.log(proxy.name); // ログ: アクセス: name → "Alice"
console.log(proxy.age); // ログ: アクセス: age → 20
JavaScriptもしここで Reflect.get を使わないと、target[prop] としか書けませんが、
ゲッターや継承などの「細かい仕様」を正しく再現するのが難しくなります。
Reflect.get は、「言語仕様的に正しい get の挙動」をまとめて引き受けてくれるので、
Proxy の中で素直に「標準の動きに任せる」ことができます。
例:set でバリデーション+本来の set を呼ぶ
const user = { name: "Alice", age: 20 };
const handler = {
set(target, prop, value, receiver) {
console.log(`set: ${String(prop)} = ${value}`);
if (prop === "age" && (typeof value !== "number" || value < 0)) {
console.log("不正な age の値です");
return false;
}
return Reflect.set(target, prop, value, receiver);
},
};
const proxy = new Proxy(user, handler);
proxy.age = 30; // OK, set: age = 30
proxy.age = -1; // 不正な age の値です
JavaScriptここでも、Reflect.set を使うことで
「基本の動き」は Reflect に任せ、
自分はバリデーションやログに集中できます。
ここが重要です。
Proxy の役割:操作を横取りする。
Reflect の役割:横取りした後で「元の正しい操作」を実行するための道具。
このコンビを理解すると、
「Proxy の handler の中では、とりあえず Reflect.xxx を呼んでおけば、基本挙動は壊さない」
という安心感が生まれます。
初心者は Reflect をどこまで理解しておくといいか
最低限のライン
今の段階で押さえておけば十分、というラインはここです。
Reflect は「オブジェクト操作の標準 API 集合」で、Reflect.get / set / has / deleteProperty などがある。
従来の obj[prop], prop in obj, delete obj[prop] などの「演算子での操作」を、関数として呼び出せるようにしたもの。
Proxy とセットで使うことが多く、trap の中で Reflect.xxx を呼ぶことで、「横取りしたあとに本来の挙動を呼び出す」ことができる。
余裕が出てきたら触るポイント
少し慣れてきたら、次を試してみると理解が深まります。
Reflect.ownKeys を使って、Symbol を含めたすべてのキーを取得する。
Reflect.defineProperty と Object.defineProperty の返り値の違い(true/false vs オブジェクト)を確認してみる。
Reflect.apply を Proxy の apply trap と組み合わせて、関数呼び出しにフックをかけつつ元の関数を呼ぶ。
まとめ
Reflect の本質は、
「オブジェクトや関数への基本的な操作を、統一された“関数 API”として提供する ES6 のメタプログラミング用の道具箱」
であることです。
押さえておきたいポイントは次の通りです。
従来の演算子や Object.xxx による操作を、Reflect.get / set / has / deleteProperty / defineProperty / ownKeys / apply などとして関数で呼び出せる。
戻り値が true/false でそろっているメソッドも多く、成功/失敗を扱いやすい。
Proxy の中で「標準の挙動に委譲したい」ときに、Reflect.xxx を使うのが定番パターン。
「新しいことをできるようにする」というより、「既存の操作を整理し、Proxy などと組み合わせやすくした」位置づけ。
最初の一歩としては、
小さなオブジェクトに対して
Reflect.get(obj, "foo");
Reflect.set(obj, "foo", 123);
Reflect.has(obj, "foo");
Reflect.deleteProperty(obj, "foo");
JavaScriptを実際に手で打ってみて、
「普段やっていることの“関数版”なんだな」という感覚を掴んでみてください。
そのうえで Proxy のコードを見たときに、Reflect.get, Reflect.set を見つけて
「あ、ここで本来の挙動を呼んでいるんだな」と分かれば、
Reflect はもうあなたの中で「難しい謎のオブジェクト」ではなく、
「Proxy と並んで使う、わりと地味だけど頼れる相棒」に変わっているはずです。
