JavaScriptのProxyがよくわからないという人向けにProxyはどのように使用することができるのかを簡単な例を通して説明を行っています。

ProxyとReflectはVue3のReactivityなどに利用されている機能でさまざまなライブラリやフレームワークでも使われています。

JavaScriptのProxyとは

Proxyには”代理”という意味があり、オブジェクトに対して直接ではなくProxyを経由してアクセスを行うことで本来の処理を変更したり、追加の処理を加えること(通常のオブジェクトの処理に対して割り込みを行い本来の振る舞いを変える)ができます。

あるサイトのProxyの例をこんな身近な例を利用して記述していました。近所のカレー屋は直接出前を注文することができますがUberを経由しても行うことができます。どちらも”最終的にはカレーが宅配されます”がUberを経由させることで到着までのステータスをリアルタイムに確認したり店の評価をつけたりと追加な機能を利用することができます。このUberがProxyと考えることできます。なんとなくわかりましたかね。

Proxyの説明をどの文書を読んだだけでは普通の人なら??だと思います。実際にコードで確認しましょう。

Proxyを使った簡単な例

userオブジェクトを作成し、nameプロパティの値を取得したい場合通常は下記の方法で取得することができます。


 let user = {
  firstName: "John"
}

console.log(user.firstName)

Proxyを利用する場合はnew Proxyを使って新たにProxyオブジェクトproxy_userを作成し、作成したproxy_userを経由してアクセスを行います。


let user = {
  firstName: "John"
}

let proxy_user = new Proxy(user,{})

console.log(proxy_user.firstName)

実行すると結果はどちらもJohnが表示されます。

Proxyの構文は以下のようになり上記では{}空のオブジェクトの部分をhandlerと呼びここに処理を加えていきます。

let proxy = new Proxy(target, handler)

handlerの中には traps(トラップ)と呼ばれる予め決められたメソッドを記述することがで処理を追加することができます。trapsには値を取得する場合に利用できるgetメソッド、値を設定したい場合に利用できるsetメソッドが存在します。

その他のメソッドについてはMDN web docsに記述されています。

getとsetメソッドともう一つdeletePropertyメソッドの説明を行っていきます。

getメソッドを使って処理を追加

handlerの中にオブジェクトの値を取得する際に利用できるgetメソッドを使って新たな処理を追加します。


let user = {
  firstName: "John"
}

let proxy_user = new Proxy(user,{
  get(target,prop){
    console.log(target +'の' + prop +'へのアクセスに追加の処理を加えました')
    return target[prop]
  }
})

console.log(proxy_user.name)

handler{}の中にgetメソッドを追加しています。getの引数にはtarget, propがありますがtargetはuser, propはproxy_user.nameで指定したnameに対応します。

Proxyを経由したアクセスは先ほどと同じでproxy_user.nameと記述します。handlerの中にgetメソッドを追加するとproxy_user.nameでnameプロパティにアクセスする際に自動的にgetメソッドが実行され下記のメッセージが表示されます。


[object Object]のfirstNameへのアクセスに追加の処理を加えました
John

Proxyを経由してuserオブジェクトのnameにアクセスすることで追加の処理を加えることができました。

setメソッドを使って処理の追加

通常のオブジェクトへの値の設定方法を確認しておきます。userオブジェクトにlastNameプロパティを追加してその値をDoeとしています。


let user = {
  firstName: "John"
}

user.lastName = 'Doe';

console.log(user.lastName)

これをsetメソッドを利用して設定します。


proxy_user.lastName = 'Doe';

console.log(proxy_user.lastName)

次にhandlerにsetメソッドを追加します。


let proxy_user = new Proxy(user,{
  get(target,prop){
    console.log(target +'の' + prop +'へのアクセスに追加の処理を加えました')
    return target[prop]
  },
  set(target,prop,value){
    console.log(target + 'に' + '新たにプロパティ' + prop + 'と値' + value + '追加')
    target[prop] = value
  }
})

setでは引数にtargetとpropだけではなくvalueも必要です。 targetはuser, propはlastNameでvalueはDoeに対応します。


proxy_user.lastName = 'Doe';

console.log(proxy_user.lastName)

実行するとsetメソッドのconsole.logの結果とproxy_user.lastNameでgetメソッドが実行されるため下記のメッセージが表示されます。


[object Object]に新たにプロパティlastNameと値Doe追加
[object Object]のlastNameへのアクセスに追加の処理を加えました
Doe

deletePropertyメソッドの使い方

オブジェクトのプロパティを削除する際に利用することができるメソッドがdeletePropertyメソッドです。新たに追加したmiddlenameプロパティを削除します。


let user = {
  firstName: "John",
  middleName: "M"
}

let proxy_user = new Proxy(user,{
  get(target,prop){
    console.log(target +'の' + prop +'へのアクセスに追加の処理を加えました')
    return target[prop]
  },
  set(target,prop,value){
    console.log(target + 'に' + '新たにプロパティ' + prop + 'と値' + value + '追加')
    target[prop] = value
  },
  deleteProperty(target,prop){
    console.log(target + 'から' + 'プロパティ' + prop + 'を削除') 
    delete target[prop]
  }
})

delete proxy_user.middleName;

実行すると”[object Object]からプロパティmiddleNameを削除”のメッセージが表示されます。

Reflectの使い方

Reflectを利用すると下記の方法でオブジェクトのプロパティの値を取得することができます。


let user = {
  firstName: "John",
  lastName: "Doe"
}

console.log(Reflect.get(user,'firstName')) //John

ReflectとProxyを一緒に利用する

Reflectを利用することで先ほどのgetメソッドやsetメソッドを書き換えることができます。


let user = {
  firstName: "John",
  lastName: "Doe"
}

let proxy_user = new Proxy(user,{
  get(target,prop,receiver){
    return Reflect.get(target,prop,receiver)
  },
})

console.log(proxy_user.firstName)

getメソッドの第3引数にreceiverが追加されていますが、receiverはtargetオブジェクトそのものでconsole.log(receiver)で中身を確認することができます。


let user = {
  firstName: "John",
  lastName: "Doe"
}

let proxy_user = new Proxy(user,{
  get(target,prop,receiver){
    console.log(receiver) //receiverの中身を確認するために追加
    return Reflect.get(target,prop,receiver)
  },
})

console.log(proxy_user.firstName)

実行するとreceiverの中身を確認できます。


{ firstName: 'John', lastName: 'Doe' }

setメソッドの中身はReflectを利用すると下記のように変更を行うことができます。


  set(target,prop,value,receiver){
    return Reflect.set(target,prop,value,receiver)
  },