JavaScript(ES2015〜)のProxyで、プロパティにフックする正しいやり方
この記事について
ES2015(ES6)で追加されたProxyオブジェクトを使用すると、プロパティへのアクセスに割り込んで、好きな処理を行うことができます。
ただし、使い方に少しコツがあります。この記事では、ネット上で散見される「良くない例」について、なにがどう良くないのかを説明して、気をつけるべきポイントを紹介します。
良くない例
こういう感じのコードが紹介されている場合がありますが、あまり良くありません。
/*
常に値を2倍にするProxy
*/
function doubleProxy_Bad(target) {
return new Proxy(target, {
get(target, name) {
//もとのプロパティの値を取得
const orig = target[name];
//2倍にして返す
return orig * 2;
},
set(target, name, value) {
//値を2倍にする
const modified = value * 2;
//もとのプロパティに設定
target[name] = modified;
}
});
}
正しい例
より正しくは、こうです。
function doubleProxy_Good(target) {
return new Proxy(target, {
get(target, name, receiver) {
//もとのプロパティの値を取得
const orig = Reflect.get(target, name, receiver);
//2倍にして返す
return orig * 2;
},
set(target, name, value, receiver) {
//値を2倍にする
const modified = value * 2;
//もとのプロパティに設定
Reflect.set(target, name, modified, receiver);
}
});
}
違いは次の2点です。
- receiver引数を正しく処理している(get/setの最後の引数)
- Reflectオブジェクトを使っている
get/set には receiver という引数がある
receiverの正体は、Proxyオブジェクトそのものです。次のコードで確認できます。
const o = { a: 10 };
//どんなプロパティにアクセスしてもreceiverを返す
const p = new Proxy(o, {
get(target, name, receiver) {
return receiver;
}
});
console.log(p.a === p);
一見すると、この引数が何のために存在するのか、わからないかもしれません。しかしgetter/setterが定義されている場合には、大きな違いが出ます。
次のような例を考えましょう。
const obj = {
a: 1,
b: 5,
get c() {
return this.a + this.b;
}
};
const p = doubleProxy(obj);
console.log(p.c); //=> ???
p.c
の結果は、12でしょうか?24でしょうか?
言いかえると、get c
の中のthis
は、もとのオブジェクトでしょうか?それともProxyでしょうか?
最初に紹介した「良くない例」だと、this
は必ずもとのオブジェクトになり、結果は12になります。「正しい例」では、this
はProxyになり、24になります。
console.log(doubleProxy_Bad(obj).c); //=> 12
console.log(doubleProxy_Good(obj).c); //=> 24
どちらの動作が望ましいかは、ケースバイケースです。しかしProxyでないと困る、というケースはあるでしょう。
たとえばgetter/setterの中でメソッドを呼び出す場合には、this
の値によって、そのメソッド呼び出しにもProxyの効果が及ぶかどうかが決まります。
Reflectオブジェクトを使おう
receiver引数とReflectオブジェクトは、そのためにあります。
Reflect.get(target, name[, receiver]);
Reflect.set(target, name, value[, receiver]);
Reflect.get
とReflect.set
の引数にreceiverを与えると、その値がgetter/setter呼び出しのthis
になります。
省略すると、targetがthis
になります。つまりもとのオブジェクトを直接参照するのと同じです。
どちらが自分の望む動作か、よく考えて使いわけましょう。
まとめ
ProxyとReflectは、セットで覚えておきましょう。get/setに限らず、両者のAPIは統一されており、組み合わせて使うよう設計されています。
「this
が何を指しているか」は、JavaScriptを書く上で、常に意識すべきポイントです。getter/setterという、比較的新しい要素についても、忘れないようにしましょう。
参考URL
ES2015(ES6)
- 9.5 Proxy Object Internal Methods and Internal Slots
- 26.1 The Reflect Object
- 9.1.8 [[Get]] (P, Receiver)
ES2016(ES7)
- 9.5 Proxy Object Internal Methods and Internal Slots
- 26 Reflection
- 9.1.8.1 OrdinaryGet (O, P, Receiver)
ES Wiki
Author And Source
この問題について(JavaScript(ES2015〜)のProxyで、プロパティにフックする正しいやり方), 我々は、より多くの情報をここで見つけました https://qiita.com/tkykmw/items/6981edef82fed25d370a著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .