この記事では主に ES6 Proxy の使用シナリオを紹介し、参考にさせていただきます。
ES6 のアロー関数、配列の分割、残りのパラメータなどの機能は、実装されるとすぐに広く広まりましたが、一方では、ブラウザの互換性のため、プロキシなどの機能は開発者によって使用されることはほとんどありませんでした。もう 1 つの側面は、これらの機能を活用するには、開発者が使用シナリオを深く理解する必要があるということです。個人的には、ES6 の Proxy がとても気に入っています。これにより、オブジェクトへの外部アクセスを簡潔かつわかりやすい方法で制御できるからです。以下では、まずProxyの使い方を紹介し、次にProxyの利用シーンを具体例を挙げて説明します。
プロキシは、その名前が示すように、デザイン パターンのプロキシ パターンに非常に似ています。このパターンは、次の 3 つの側面でよく使用されます:
オブジェクトへの外部アクセスの傍受と監視
効率の低下関数またはクラスの複雑さ
複雑な操作の前に操作を検証するか、必要なリソースを管理します
プロキシをサポートするブラウザ環境では、プロキシはグローバル オブジェクトであり、直接使用できます。 Proxy(target, handler) はコンストラクター、target はプロキシされるオブジェクト、handler はさまざまなプロキシ操作を宣言し、最終的にプロキシ オブジェクトを返すオブジェクトです。外部の世界がプロキシ オブジェクトを通じてターゲット オブジェクトのプロパティにアクセスするたびに、ハンドラー オブジェクトを通過するため、プロキシ オブジェクトはミドルウェアと非常によく似ています。では、プロキシはどのような操作を傍受できるのでしょうか?最も一般的な操作は、オブジェクト属性の取得 (読み取り)、設定 (変更) およびその他の操作です。インターセプト可能な操作の完全なリストについては、ここをクリックしてください。さらに、Proxy オブジェクトには、すべてのプロキシ操作をいつでもログアウトするための revoke メソッドも用意されています。 Proxy を正式に導入する前に、Reflect についてある程度理解しておくことをお勧めします。詳細については、「MDN Reflect」を参照してください。
基本
const target = { name: 'Billy Bob', age: 15 }; const handler = { get(target, key, proxy) { const today = new Date(); console.log(`GET request made for ${key} at ${today}`); return Reflect.get(target, key, proxy); } }; const proxy = new Proxy(target, handler); proxy.name; // => "GET request made for name at Thu Jul 21 2016 15:26:20 GMT+0800 (CST)" // => "Billy Bob"
上記のコードでは、最初にプロキシ ターゲット オブジェクト target を定義し、次にすべてのプロキシ操作を含むハンドラー オブジェクトを宣言し、Proxy(target, handler) を使用してプロキシ オブジェクト proxy へのすべてのアクセスを作成します。プロキシを使用したターゲット属性はハンドラーによって処理されます。
1. 検証モジュールを抽出します
この例では、プロキシを使用してデータ型の精度を確認する方法を示します。属性のバリデーターはコード構造をすぐに肥大化させる可能性があります。プロキシを使用すると、バリデーターをコアロジックから分離し、自己完結型にすることができます:
let numericDataStore = { count: 0, amount: 1234, total: 14 }; numericDataStore = new Proxy(numericDataStore, { set(target, key, value, proxy) { if (typeof value !== 'number') { throw Error("Properties in numericDataStore can only be numbers"); } return Reflect.set(target, key, value, proxy); } }); // 抛出错误,因为 "foo" 不是数值 numericDataStore.count = "foo"; // 赋值成功 numericDataStore.count = 333;
バリデーターとメインロジックを分離することで、コンテンツを無限に拡張できます。 personValidators バリデーターは、関連するクラスや関数に直接的なダメージを与えません。さらに複雑にするために、プロキシを使用して型チェックをシミュレートし、関数が正しい型と数値のパラメーターを受け取るかどうかをチェックすることもできます。変数 名前の前にアンダースコア _ を追加して、これがプライベート プロパティ (実際にはプライベートではない) であることを示しますが、実際に誰もアクセスしたり変更したりしないことを保証することはできません。以下のコードでは、API オブジェクト内でのメソッド呼び出しを容易にするためにプライベート apiKey を宣言していますが、外部から API にアクセスできるようにしたくありません。_apiKey:
function createValidator(target, validator) { return new Proxy(target, { _validator: validator, set(target, key, value, proxy) { if (target.hasOwnProperty(key)) { let validator = this._validator[key]; if (!!validator(value)) { return Reflect.set(target, key, value, proxy); } else { throw Error(`Cannot set ${key} to ${value}. Invalid.`); } } else { throw Error(`${key} is not a valid property`) } } }); } const personValidators = { name(val) { return typeof val === 'string'; }, age(val) { return typeof age === 'number' && age > 18; } } class Person { constructor(name, age) { this.name = name; this.age = age; return createValidator(this, personValidators); } } const bill = new Person('Bill', 25); // 以下操作都会报错 bill.name = 0; bill.age = 'Bill'; bill.age = 15;
明らかに、規約には拘束力がありません。 ES6 プロキシを使用すると、実際のプライベート変数を実装できます。以下に、さまざまな読み取り方法に対する 2 つの異なるプライベート化方法を示します。 1 つ目の方法は、set / get を使用して読み取りおよび書き込みリクエストをインターセプトし、未定義を返すことです:
let obj = { pickyMethodOne: function(obj, str, num) { /* ... */ }, pickyMethodTwo: function(num, obj) { /*... */ } }; const argTypes = { pickyMethodOne: ["object", "string", "number"], pickyMethodTwo: ["number", "object"] }; obj = new Proxy(obj, { get: function(target, key, proxy) { var value = target[key]; return function(...args) { var checkArgs = argChecker(key, args, argTypes[key]); return Reflect.apply(value, target, args); }; } }); function argChecker(name, args, checkers) { for (var idx = 0; idx < args.length; idx++) { var arg = args[idx]; var type = checkers[idx]; if (!arg || typeof arg !== type) { console.warn(`You are incorrectly implementing the signature of ${name}. Check param ${idx + 1}`); } } } obj.pickyMethodOne(); // > You are incorrectly implementing the signature of pickyMethodOne. Check param 1 // > You are incorrectly implementing the signature of pickyMethodOne. Check param 2 // > You are incorrectly implementing the signature of pickyMethodOne. Check param 3 obj.pickyMethodTwo("wopdopadoo", {}); // > You are incorrectly implementing the signature of pickyMethodTwo. Check param 1 // No warnings logged obj.pickyMethodOne({}, "a little string", 123); obj.pickyMethodOne(123, {});
2 つ目の方法は、オペレーションで has to intercept を使用することです:var api = {
_apiKey: '123abc456def',
/* mock methods that use this._apiKey */
getUsers: function(){},
getUser: function(userId){},
setUser: function(userId, config){}
};
// logs '123abc456def';
console.log("An apiKey we want to keep private", api._apiKey);
// get and mutate _apiKeys as desired
var apiKey = api._apiKey;
api._apiKey = '987654321';
実行速度が遅い、または実行環境のリソースを多く占有する属性やインターフェイスの場合、開発者はその使用状況やパフォーマンスを記録する必要があります。このとき、プロキシをミドルウェアとして使用し、ログ機能を簡単に実装できます:
let api = { _apiKey: '123abc456def', getUsers: function(){ }, getUser: function(userId){ }, setUser: function(userId, config){ } }; const RESTRICTED = ['_apiKey']; api = new Proxy(api, { get(target, key, proxy) { if(RESTRICTED.indexOf(key) > -1) { throw Error(`${key} is restricted. Please see api documentation for further info.`); } return Reflect.get(target, key, proxy); }, set(target, key, value, proxy) { if(RESTRICTED.indexOf(key) > -1) { throw Error(`${key} is restricted. Please see api documentation for further info.`); } return Reflect.get(target, key, value, proxy); } }); // 以下操作都会抛出错误 console.log(api._apiKey); api._apiKey = '987654321';
他の開発者に noDelete 属性を削除させたくない、そして、oldMethod を呼び出す開発者にこのメソッドが廃止されたことを知らせるか、開発者に doNotChange 属性を変更しないように指示したいとします。
var api = { _apiKey: '123abc456def', getUsers: function(){ }, getUser: function(userId){ }, setUser: function(userId, config){ } }; const RESTRICTED = ['_apiKey']; api = new Proxy(api, { has(target, key) { return (RESTRICTED.indexOf(key) > -1) ? false : Reflect.has(target, key); } }); // these log false, and `for in` iterators will ignore _apiKey console.log("_apiKey" in api); for (var key in api) { if (api.hasOwnProperty(key) && key === "_apiKey") { console.log("This will never be logged because the proxy obscures _apiKey...") } }
この時点で、ファイルがチャンクで送信されている場合、一部の操作は多くのリソースを占有します。新しいリクエストに応答する必要はありません (絶対的なものではありません)。現時点では、プロキシを使用してリクエストの特性を検出し、特性に基づいて応答を必要としないものと応答を必要とするものを除外できます。次のコードは、完全なコードではありませんが、その素晴らしさは理解できると思います。
プロキシの中断は、任意の時点でのターゲットのプロキシのキャンセルをサポートします。データやインターフェースへのアクセスを完全に遮断する操作です。次の例では、Proxy.revocable メソッドを使用して、取り消し可能なプロキシを持つプロキシ オブジェクトを作成します。ES7 で実装された Decorator は、デザイン パターンの Decorator パターンに相当します。 Proxy と Decorator の使用シナリオを単純に区別すると、次のように要約できます。 Proxy の中核機能はプロキシ内部への外部アクセスを制御することであり、Decorator の中核機能はプロキシの機能を拡張することです。デコレーター。主要な使用シナリオが区別されている限り、アクセス ログなどの機能は、この記事ではプロキシを使用して実装されていますが、開発者はプロジェクトのニーズ、チームの仕様、および独自の好みに基づいて実装することもできます。選択の。
上記は私があなたのためにまとめたものです。
関連記事:
以上がES6 でプロキシを使用する手順の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。