【前半】ProxyオブジェクトとReflectオブジェクト
どうもフロントエンドエンジニアのoreoです。2回に分けてProxy
オブジェクトとReflect
オブジェクトに関して整理したいと思います。前半のこの記事では、Proxyオブジェクト
に関して記載します。
1 Proxyオブジェクトとは?
プロパティーの操作(取得など)に独自の処理を追加するためのオブジェクトです。
基本的な構文として、new Proxy()
の第一引数に独自処理を追加したいオブジェクト、第二引数には第一引数に渡したオブジェクトに対して操作するメソッドを格納したオブジェクト(ハンドラーと呼ばれます)を渡します。
/**
* @param target 独自処理を追加したいオブジェクト
* @param handler targetに対して操作するメソッドを格納したオブジェクト
*/
const proxy = new Proxy(target, handler);
ハンドラー内で使用するメソッドは、トラップと呼ばれ主に下記のようなものがあります。
1-1 setトラップ
プロパティーの更新が行われた時に独自の処理を追加できます。set
トラップには、target
、prop
、value
、receiver
の4つの引数を渡します。以下の例では、プロパティーa
を更新しようとすると、console.log
が呼ばれます。
const targetObj = { a: 0 };
/**
* @param target new Proxy()の第一引数に渡される独自処理を追加したいオブジェクト
* @param prop アクセスされたプロパティーの名前
* @param value 更新したい値
* @param receiver new Proxy()でインスタンス化されたオブジェクト
*/
const handler = {
set: function (target, prop, value, receiver) {
console.log(`${prop}を${value}に変更。setトラップ発動`);
target[prop] = value; //値を更新
}
};
const proxy = new Proxy(targetObj, handler);
proxy.a = 100; //「aを100に変更。setトラップ発動」が出力
1-2 getトラップ
プロパティーの取得が行われた時に独自の処理を追加できます。get
トラップには、target
、prop
、receiver
の3つの引数を渡します。以下の例では、プロパティーa
にアクセスすると、console.log
が呼ばれます。
const targetObj = { a: 0 };
/**
* @param target new Proxy()の第一引数に渡される独自処理を追加したいオブジェクト
* @param prop アクセスされたプロパティーの名前
* @param receiver new Proxy()でインスタンス化されたオブジェクト
*/
const handler = {
get: function (target, prop, receiver) {
console.log(`${prop}にアクセス。getトラップ発動`);
return target[prop]; //アクセスされたプロパティーを返す
}
};
const proxy = new Proxy(targetObj, handler);
proxy.a; //「aにアクセス。getトラップ発動」が出力
1-3 deletePropertyトラップ
値が削除された時に独自の処理を追加できます。deleteProperty
トラップには、target
、prop
2つの引数を渡します。以下の例では、delete
演算子でプロパティーa
にの値を削除したときに、console.log
が呼ばれます。
const targetObj = { a: 0 };
/**
* @param target new Proxy()の第一引数に渡される独自処理を追加したいオブジェクト
* @param prop アクセスされたプロパティーの名前
*/
const handler = {
deleteProperty: function (target, prop) {
console.log(`${prop}の値を削除。deletePropertyトラップ発動`);
delete target[prop]; //値を削除
}
};
const proxy = new Proxy(targetObj, handler);
delete proxy.a; //「aの値を削除。deletePropertyトラップ発動」が出力
1-4 その他トラップ
その他のトラップは👇をご参照ください。
2 具体的な使用例
2-1 プロパティの更新を許可しない(バリデーションも)
set
トラップの中で、プロパティの更新処理を書かず、throw new Error
などをしてあげると、プロパティの更新を制限できます。
const targetObj = { a: 0 };
const handler = {
set: function () {
throw new Error("プロパティの更新はできません")
}
};
const proxy = new Proxy(targetObj, handler);
proxy.a = 100; //エラー「プロパティの更新はできません」
同じような考え方で、バリデーションもできます。下の例では、Number型以外でのプロパティの更新を許可しないようにしています。
const targetObj = { a: 0 };
const handler = {
set: function (target, prop, value) {
if (typeof value === "number") {
target[prop] = value;
} else {
throw new Error("Number型でなければ更新はできません");
}
},
};
const proxy = new Proxy(targetObj, handler);
proxy.a = 100; //Number型であれば更新できる。
console.log(proxy.a); //100
proxy.a = "ああああ"; //エラー「Number型でなければ更新はできません」
2-2 値の削除を許可しない
2-1のset
トラップと同様に、deleteProperty
トラップの中で、値の削除処理を書かず、throw new Error
などをしてあげると、値の削除を制限できます。
const targetObj = { a: 0 };
const handler = {
deleteProperty: function () {
throw new Error("値の削除はできません")
}
};
const proxy = new Proxy(targetObj, handler);
delete proxy.a; //エラー「値の削除はできません」
2-3 デフォルト値を返すようにする
get
トラップを利用すると、アクセスされたプロパティーが見つからない場合に、デフォルト値を返すように設定することが可能です。
const targetObj = { a: 0 };
const handler = {
get: function (target, prop, receiver) {
if (prop in target) { //プロパティーのチェック
return target[prop];
} else {
return "hoge"; //プロパティーが見つからなければ、デフォルト値を返す
}
},
};
const proxy = new Proxy(targetObj, handler);
console.log(proxy.a);
console.log(proxy.b);
最後に
Vue.jsのソースコードなどでよく見るProxy
オブジェクトに関して、整理しました。オブジェクトの操作を拡張してくれる非常に強力なツールですね。
後半の記事(👇)では、Reflect
オブジェクトに関してProxy
オブジェクトとの併用方法なども含めて整理したいと思います!
Discussion