コンテンツにスキップ

JavaScript/Proxy

出典: フリー教科書『ウィキブックス(Wikibooks)』


Proxy オブジェクトは、ターゲットオブジェクトの基本的な操作(プロパティの検索、割り当て、列挙、関数呼び出しなど)をカスタマイズするための特別なオブジェクトです。プロキシを使用すると、オブジェクトの動作を傍受し、変更することができます[1]

構文

[編集]
const proxy = new Proxy(target, handler)
  • target: プロキシが仮想化するターゲットオブジェクト。
  • handler: プロパティへのアクセスが傍受されたときに呼び出されるメソッド(トラップ)を含むオブジェクト。

プロパティとメソッド

[編集]

静的プロパティ

[編集]
静的プロパティ
名称 解説
Proxy.length Proxyコンストラクタが受け取る引数の数を返します。常に2です。
Proxy.name Proxyコンストラクタの名前を返します。常に"Proxy"です。

静的アクセサ

[編集]

静的メソッド

[編集]
静的プロパティ
名称 解説
Proxy.revocable() 取り消し可能なProxyオブジェクトを作成します。

[編集]

基本的なプロキシの作成例

[編集]

以下のプログラムは、基本的なプロキシを作成して、オブジェクトのプロパティアクセスを傍受する例です。

const target = {
  message: 'hello',
  getGreeting() {
    return this.message;
  }
};

const handler = {
  get(target, prop, receiver) {
    console.log(`プロパティ "${prop}" へのアクセスが検出されました`);
    return Reflect.get(target, prop, receiver);
  }
};

const proxy = new Proxy(target, handler);

console.log(proxy.message); // プロパティ "message" へのアクセスが検出されました、hello
console.log(proxy.getGreeting()); // プロパティ "getGreeting" へのアクセスが検出されました、hello

このプログラムでは、get トラップを使用してオブジェクトのプロパティにアクセスするたびにメッセージをログに記録しています。

プロパティの検証例

[編集]

以下のプログラムは、プロキシを使用してオブジェクトのプロパティに対する検証を実装する例です。

const person = {
  name: 'John',
  age: 30
};

const validator = {
  set(target, prop, value, receiver) {
    if (prop === 'age') {
      if (typeof value !== 'number') {
        throw new TypeError('年齢は数値である必要があります');
      }
      if (value < 0 || value > 120) {
        throw new RangeError('年齢は0から120の間である必要があります');
      }
    }
    return Reflect.set(target, prop, value, receiver);
  }
};

const validatedPerson = new Proxy(person, validator);

validatedPerson.age = 25; // 正常に動作
console.log(validatedPerson.age); // 25

// 以下はエラーを投げます
// validatedPerson.age = -5; // RangeError: 年齢は0から120の間である必要があります
// validatedPerson.age = '30'; // TypeError: 年齢は数値である必要があります

このプログラムでは、set トラップを使用して age プロパティに対する検証を実装しています。値が数値でない場合や有効な範囲外の場合はエラーが発生します。

隠しプロパティの実装例

[編集]

以下のプログラムは、プロキシを使用して隠しプロパティを実装する例です。

const target = {
  _secret: 'これは秘密の値です',
  publicData: '公開データ'
};

const handler = {
  get(target, prop, receiver) {
    if (prop.startsWith('_')) {
      return undefined; // 隠しプロパティへのアクセスを拒否
    }
    return Reflect.get(target, prop, receiver);
  },
  
  set(target, prop, value, receiver) {
    if (prop.startsWith('_')) {
      throw new Error('隠しプロパティを変更することはできません');
    }
    return Reflect.set(target, prop, value, receiver);
  },
  
  has(target, prop) {
    if (prop.startsWith('_')) {
      return false; // 隠しプロパティの存在を隠す
    }
    return Reflect.has(target, prop);
  },
  
  ownKeys(target) {
    return Reflect.ownKeys(target).filter(key => !key.startsWith('_'));
  }
};

const secureObject = new Proxy(target, handler);

console.log(secureObject.publicData); // "公開データ"
console.log(secureObject._secret); // undefined
console.log('_secret' in secureObject); // false
console.log(Object.keys(secureObject)); // ["publicData"]

// 以下はエラーを投げます
// secureObject._secret = '新しい秘密'; // Error: 隠しプロパティを変更することはできません

このプログラムでは、複数のトラップを使用して、アンダースコアで始まるプロパティへのアクセス、変更、存在確認、列挙を制限しています。

関数プロキシの例

[編集]

以下のプログラムは、関数にプロキシを適用して、その呼び出しを傍受する例です。

function sum(a, b) {
  return a + b;
}

const handler = {
  apply(target, thisArg, argumentsList) {
    console.log(`関数が呼び出されました。引数: ${argumentsList}`);
    const result = Reflect.apply(target, thisArg, argumentsList);
    console.log(`結果: ${result}`);
    return result;
  }
};

const proxiedSum = new Proxy(sum, handler);

const result = proxiedSum(1, 2);
// 関数が呼び出されました。引数: 1,2
// 結果: 3
console.log(result); // 3

このプログラムでは、apply トラップを使用して関数呼び出しを傍受し、引数と結果をログに記録しています。

注意点

[編集]
  • 不変条件: プロキシには、オブジェクトの不変条件を維持するための制約があります。これらの制約に違反するとエラーが発生します。
  • リボケーション: Proxy.revocable() を使用して取り消し可能なプロキシを作成できます。
  • this バインディング: プロキシ内のメソッドにおける this の値に注意が必要です。
  • パフォーマンス: プロキシの使用はパフォーマンスに影響を与える可能性があります。
  • ES2015+: プロキシは ECMAScript 2015 (ES6) で導入されました。

脚註

[編集]
  1. ^ プロキシはメタプログラミングやオブジェクトの仮想化に役立つ強力な機能です。

外部リンク

[編集]