すべてのプロパティでセッターが呼ばれたときに処理を追加したい
すべてのプロパティでセッターが呼ばれたときに処理を追加したいのですが、addEventListenerを経由するとうまくいきません。
javascript
1const Target = class { 2 constructor(element) { 3 this._element = element 4 element.addEventListener('focus', this) 5 element.addEventListener('blur', this) 6 } 7 get element() { 8 return this._element 9 } 10 get active() { 11 return !!this._active 12 } 13 set active(bool) { 14 this._active = bool 15 } 16 handleEvent({ type }) { 17 console.log(type + ' before', this.active) 18 this.active = type === 'focus' 19 console.log(type + ' after', this.active) 20 } 21} 22 23const handler = { 24 get(target, key) { 25 return target[ key ] 26 }, 27 set(target, key, value) { 28 console.log('YES!', { target, key, value }) //何らかの処理 29 target[ key ] = value 30 return true 31 } 32} 33 34const element = document.body.appendChild(document.createElement('textarea')) 35 36const proxiedTarget = new Proxy(new Target(element), handler) 37 38proxiedTarget.active = !proxiedTarget.active //OK 39 40proxiedTarget.active = !proxiedTarget.active //OK 41 42proxiedTarget.handleEvent({ type: 'focus' }) //OK //addEventListener用ですが確認のため直接実行 43 44//クリック等で要素をフォーカス・ブラーさせてaddEventListener経由でhandleEventが実行されるとhandler.setが反応しない><
上記の例ではプロパティは「active」一つだけですが実際は(継承元も含めて)たくさんあるのでProxyで一括処理できないかなと思いました。
が、handleEventメソッドを直接呼ぶ場合はメソッド内の「this.active = type === 'focus'」に反応して期待通りにhandlerの処理が行われるのですが、
addEventListener経由で呼ばれる場合はProxyにラップされていないのかhandlerの処理が行われません。
addEventListener経由でもトラップするにはどうすればいいですか?
あと、
javascript
1const proxiedTarget = new Proxy(new Target(element), handler)
ではなく
javascript
1const ProxiedTarget = new Proxy(Target, handler) 2const proxiedTarget = new ProxiedTarget(element)
みたいにできますか?(Proxyのターゲットとしてインスタンスじゃなくクラスを渡しても実現可能ですか?)
やりたいことがもっと短いコードでかんたんに実現できるならProxyじゃなくても何でもいいです。
想像力を要求される質問な気がしますが、
Q1. _acitveのような _ で始まるプロパティはプライベートプロパティの代替ですか
Q2. focus,blurイベントハンドラで _active プロパティの真偽値が反転するように、mouseover/mouseoutなどの対応するイベントハンドラ同士でトグル処理するのが目的ですか
サンプルのコードを削りすぎていますか?すみません。
A1. はいそんなかんじです。じっさいは「handler.set()」内で「if (key[ 0 ] !== '_')」等として選別します。
A2. focusとblurの値の反転以外は、ほかにはkeydownでキーコードを記録したり(厳密には違いますが)mousemoveで座標(とかドラッグ中かどうか等)を記録したりです。
Proxyにはぜんぜんこだわっていません。ほかに思いつかなかっただけでどんな方法でもいいです。よろしくおねがいします。
> 上記の例ではプロパティは「active」一つだけですが実際は(継承元も含めて)たくさんあるのでProxyで一括処理できないかなと思いました。
イベントタイプに対応するデータ(key/value)が多量にあるという意味なら、new Proxyを使用すれば、addEventListener処理は楽になりますが、イベント発火時に多量のデータを検索するコストを払うことになり、コストを先に払うか、後に払うかだけの違いになっている気がします。
結局、new Proxy版とそうでない版を作って、パフォーマンスを比較してみる以外にないのではないでしょうか。
大量にあるのはクラスのプロパティで、
(書き込みもできる(getだけじゃなくsetもできる)プロパティは親クラスからの継承も含めて今のところ10数個)
これらのプロパティすべての書き込みを監視したいのですが、この書き込みもできるプロパティの一部が、
外部からだけじゃなくaddEventListener経由で内部からも変更される仕組みで、このaddEventListener経由のばあいだけがうまくいかなかったので
方向としてはProxyが正解であとはaddEventListenerの箇所だけうまくやれば・・・と思ってました。
Proxyは処理が重くなるんですか。
10数個程度なら、プロパティを列挙して初期化するだけで良いのでは…。
それほど多いとは思いません。
どうもありがとうございます。その方向で進めたいと思います。
回答2件
あなたの回答
tips
プレビュー