概要
JavaScriptの Proxy
は、「あるオブジェクトに対するすべての操作」を横から捕捉し、任意の振る舞いに書き換えることができる。
つまり、ただのデータ構造だったオブジェクトが、知能を持った“反応型オブジェクト”に進化する。
Vue3のリアクティブ設計、型チェックの強化、アクセスログの記録、APIラッパーなど、あらゆる“動的挙動”の裏に潜むのが Proxy
だ。
本記事では、構文から設計まで、Proxyを使うべき場面・使わないべき場面を含めて構造的に理解する。
対象環境
ES6以降(Node.js / モダンブラウザ)
Proxyの基本構文
const proxy = new Proxy(target, handler);
-
target
: 操作対象となるオブジェクト -
handler
: どのように振る舞いを上書きするかの定義
最小構成例:ログを出力するProxy
const target = { name: 'toto' };
const handler = {
get(obj, prop) {
console.log(`GET ${prop}`);
return obj[prop];
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // ログ + 値出力
サポートされるTrap一覧
Trap名 | 捕捉対象 |
---|---|
get |
プロパティの取得 |
set |
プロパティの代入 |
has |
in 演算子 |
deleteProperty |
delete 演算子 |
apply |
関数の呼び出し |
construct |
new によるコンストラクタ呼び出し |
ownKeys |
Object.keys() など |
getOwnPropertyDescriptor |
Object.getOwnPropertyDescriptor() |
ユースケース①:動的なバリデーション
const user = {
age: 0
};
const validator = {
set(obj, prop, value) {
if (prop === 'age' && typeof value !== 'number') {
throw new TypeError('age must be a number');
}
obj[prop] = value;
return true;
}
};
const proxyUser = new Proxy(user, validator);
proxyUser.age = 30; // ✅ OK
proxyUser.age = 'young'; // ❌ TypeError
→ ✅ 型安全な設計が可能に
ユースケース②:存在しないプロパティのハンドリング
const fallback = new Proxy({}, {
get: (obj, prop) => prop in obj ? obj[prop] : `未定義:${prop}`
});
console.log(fallback.language); // '未定義:language'
→ ✅ 安全に “未定義アクセス” を検出・補完可能
ユースケース③:自動バインディング(Vueのようなリアクティブ設計)
function reactive(obj) {
return new Proxy(obj, {
get(target, prop) {
console.log(`依存追跡: ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`更新検知: ${prop} = ${value}`);
target[prop] = value;
return true;
}
});
}
const state = reactive({ count: 0 });
state.count++; // get → set → 更新検知ログ
→ ✅ Vue 3の reactive()
はまさにこの形
ユースケース④:関数呼び出しの拡張(apply)
function greet(name) {
return `Hello, ${name}`;
}
const logged = new Proxy(greet, {
apply(target, thisArg, args) {
console.log(`Call: ${args[0]}`);
return target.apply(thisArg, args);
}
});
logged('toto'); // ログ + 呼び出し
→ ✅ 関数に“副作用”を追加したいときに便利
ユースケース⑤:構築を制御(construct)
class Person {
constructor(name) {
this.name = name;
}
}
const Protected = new Proxy(Person, {
construct(target, args) {
if (!args[0]) throw new Error('name is required');
return new target(...args);
}
});
new Protected('toto'); // OK
new Protected(); // ❌ Error
→ ✅ コンストラクタの検査・注入処理に使える
注意点・落とし穴
❌ Proxyを多用するとパフォーマンスが低下する可能性
- リアクティブ構造を大規模に作る場合は
Proxy
の使いすぎに注意
❌ Proxyはオブジェクトの“見た目”を変えない
typeof proxy; // 'object'
Array.isArray(proxy); // ❌ false(元が配列でも)
→ ✅ 必要に応じて Reflect
を使って正確に模倣する
Reflectと組み合わせて安全に
const safeProxy = new Proxy(obj, {
get: (target, prop) => Reflect.get(target, prop),
set: (target, prop, value) => Reflect.set(target, prop, value)
});
→ ✅ Proxyの中で本来の操作を正しく行うために Reflect
を活用
結語
Proxy
は単なる構文ではない。
それは「JavaScriptのあらゆる振る舞いをオーバーライドできるインターセプター」であり、
設計者に “オブジェクトの真の振る舞い” を定義する力を与える。
- バリデーションの自動化
- 隠蔽的デザイン
- リアクティブ構造
- メタ的ロジックの挿入
これらをコードの中に安全に、かつ動的に埋め込むための道具が Proxy である。
設計とは「コードを書く」ことではない。
「コードがどう振る舞うか」を定義することである。