JavaScriptのProxy
オブジェクトを使うと、別のオブジェクトのプロパティの取得、設定、列挙、関数呼び出しなどを再定義できる。
使いどころ
例えば以下のようなケースに活躍する。
- プロパティへのアクセスログ。
- プロパティに設定される値のバリデーション。
- プロパティに設定される値のフォーマット。
- プロパティに設定される値のサニタイズ。
- プロパティの保護。
- などなど。
わかるような、わからないような使いどころである。外付けできるsetter
、getter
のすごいやつ、のようなイメージだろうか。
使い方
Proxy
コンストラクタは、以下の2つの引数を受け取る。
引数 | 概要 |
---|---|
target |
処理をカスタマイズしたいオブジェクト。 |
handler |
カスタマイズした処理(=関数群)を保持するオブジェクト。 |
handler
に空のオブジェクトを渡してもProxy
オブジェクトは作成される。ただし処理はインターセプトされず、元のオブジェクトの処理がそのまま実行される。
handlerオブジェクトについて
handler
オブジェクトが保持する関数群は、オリジナルのオブジェクトの処理をつかまえることから、トラップと呼ばれる。
トラップには、オブジェクトの内部メソッドに対応する名前が予めつけられている。これらの関数をhandler
オブジェクトのプロパティとして保持し、Proxy
コンストラクタに渡すことで、オリジナルの処理を再定義するわけである。
内部メソッド | 対応するトラップ |
---|---|
[[GetPrototypeOf]] |
getPrototypeOf(target) |
[[SetPrototypeOf]] |
setPrototypeOf(target, prototype) |
[[IsExtensible]] |
isExtensible(target) |
[[PreventExtensions]] |
preventExtensions(target) |
[[GetOwnProperty]] |
getOwnPropertyDescriptor(target, propertyKey) |
[[DefineOwnProperty]] |
defineProperty(target, propertyKey, attributes) |
[[HasProperty]] |
has(target, propertyKey) |
[[Get]] |
“get(target, propertyKey, receiver?)` |
[[Set]] |
set(target, propertyKey, value, receiver?) |
[[Delete]] |
deleteProperty(target, propertyKey) |
[[OwnPropertyKeys]] |
ownKeys(target) |
また、関数オブジェクトの場合は以下のようなトラップも使える。
内部メソッド | 対応するトラップ |
---|---|
[[Call]] |
apply(target, thisArgument, argumentsList) |
[[Construct]] |
construct(target, argumentsList, newTarget) |
オリジナルのオブジェクトが返す内部メソッドの値は、これらの関数が返す値によって上書きされる。
なお、トラップ内でオリジナルのオブジェクトに何らかの操作を行う場合、Reflect
オブジェクトのメソッドを通じて行うのが一般的である。
Reflect
オブジェクトとは、オブジェクトを一貫したインターフェースで操作するための静的なグローバルオブジェクトだ(Reflectオブジェクトの詳細は別ページにまとめた)。
操作対象のオブジェクトを直接操作しても結果は変わらないが、Reflect
オブジェクトの方が記法が一貫していてわかりやすい。Reflect
オブジェクト自体、handler
のトラップ内で、オリジナルのオブジェクトを操作することを主目的に用意されたようだ(そんなことがMDNに書いてあった)。
例えばgetter
を上書きする場合、以下のように書く。
参考元:Proxy() constructor – JavaScript | MDN
Proxyの格好いい使い方
VanJSという超軽量のUIフレームワークでは、Proxy
オブジェクトが効果的に使われている。
tag()
関数は簡略化しているが、おおよそのイメージは掴めるかと思う。実際にはこの中で、引数に応じて関数名に対応するDOMオブジェクトを作って返す。
Proxy
を入れ子にすることで、名前空間とタグ名を効率的にまとめていることがわかる。かっちょいい。
余談だがVanJSは140行でUIフレームワークの基本的な機能を実現している。ソースはこちら。超かっちょいい。