フロントエンド JavaScript におけるリフレクションとプロキシ

フロントエンド JavaScript におけるリフレクションとプロキシ

1. 反射とは何ですか?

リフレクションの概念は、 JavaC#などの多くのプログラミング言語に存在します。

オブジェクト指向プログラミングでは、通常、最初にクラスとメソッドが定義され、次に次の例のように、メソッドを明示的に呼び出すためのオブジェクトが作成されます。

パブリッククラスUser{
   プライベート文字列名;
   プライベート日付誕生日;
       //....
   パブリック int 誕生日による年齢計算(){
            // .....
   }
}
// ユーザーを呼び出す u = new User("jack", new Date());
誕生日による年齢を計算します。


上記の呼び出し方法はよく知られています。ただし、抽象フレームワーク (フレームワークはビジネス定義クラスと相互運用する必要があります) を作成する場合、ビジネス クラスのメンバーとメソッドがわからないため、リフレクションを使用してメンバー変数を動的に取得したり、メソッドを呼び出したりします。

次の例では、リフレクションを使用して json を Java オブジェクトに変換します。

パブリック静的クラス User {
 プライベート文字列名;
 パブリック文字列getName() {
    名前を返します。
 }
   パブリック void setName(文字列名) {
     this.name = 名前;
   }
}

// リフレクションを使用してオブジェクト セッター メソッドを呼び出します。
パブリック静的 <T> T fill(Class<T> userClass, Map<String, Object> json) 例外をスローします {
        フィールド[] fields = userClass.getDeclaredFields();
        T ユーザー = userClass.newInstance();
        for (フィールド フィールド: フィールド) {
            // 最初の文字を大文字にします String name = field.getName();
            char[] arr = name.toCharArray();
            arr[0] = Character.toUpperCase(arr[0]);
            System.out.println(新しいString(arr));
            メソッド method = userClass.getDeclaredMethod("set" + new String(arr), field.getType());
            オブジェクトの戻り値 = method.invoke(user, json.get(name));
        }
        ユーザーを返します。
}

2. JavaScriptで反映する

JavaScriptにはES6で組み込みのリフレクション オブジェクトReflectが用意されていますが、 JavaScriptのリフレクションは Java のリフレクションとは異なります。まず、 Reflectが提供する 13 個の静的メソッドを見てみましょう。

  • Reflect.apply(target, thisArg, args)
  • Reflect.construct(target, args)
  • Reflect.get(target, name, receiver)
  • Reflect.set(target, name, value, receiver)
  • Reflect.defineProperty(target, name, desc)
  • Reflect.deleteProperty(target, name)
  • Reflect.has(target, name)
  • Reflect.ownKeys(target)
  • Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target, name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype)

2.1 Reflect.get(ターゲット、名前、受信者)

Reflect.getメソッドは、対象オブジェクトの name プロパティを検索して返します。そのようなプロパティが存在しない場合は、 undefinedを返します。

定数オブジェクト = {
  名前: 'ジャック',
  年齢: 12歳
  ユーザー情報を取得する() {
    this.name + 'age is ' + this.age を返します。
  }
}

Reflect.get(obj, 'name') // ジャック
Reflect.get(obj, '年齢') // 12
Reflect.get(obj, 'userInfo') // ジャックの年齢は12歳です

// レシーバー パラメーターが渡された場合、userInfo() 関数が呼び出されると、レシーバー オブジェクトが参照されます。
定数レシーバーオブジェクト = {
  名前: 'シャオミン'、
  年齢: 22
};

Reflect.get(obj, 'userInfo', receivedObj) // Xiaoming の年齢は 22 歳です

2.2 Reflect.set(ターゲット、名前、値、レシーバー)

定数オブジェクト = {

  名前: 'ジャック',
  年齢: 12歳
  updateAge(値)を設定する{
    this.age = value を返します。
  },
}
Reflect.set(obj, '年齢', 22);
オブジェクト年齢 // 22

// レシーバー パラメーターが渡された場合、updateAge() 関数が呼び出されると、レシーバー オブジェクトが参照されます。
定数レシーバーオブジェクト = {
  年齢: 0
};

Reflect.set(obj, 'updateAge', 10, レシーバーObj) // 
オブジェクト年齢 // 22
レシーバーオブジェクト.年齢 // 10

2.3 Reflect.has(obj, name)

Reflect.hasメソッドはname in objin演算子と同等です。

定数オブジェクト = {
  名前: 'ジャック',
}
名前内のオブジェクト // true
Reflect.has(obj, 'name') // 真

2.4 Reflect.deleteProperty(obj, name)

Reflect.deletePropertyメソッドはdelete obj[name]と同等であり、オブジェクトのプロパティを削除するために使用されます。削除が成功した場合、または削除された属性が存在しない場合はtrueが返されます。削除が失敗し、削除された属性がまだ存在する場合は false が返されます。

定数オブジェクト = {
  名前: 'ジャック',
}
オブジェクト名を削除 
Reflect.deleteProperty(obj, 'name')


2.5 Reflect.construct(ターゲット、引数)

Reflect.constructメソッドはnew target(...args)と同等です。

関数 User(名前){
  this.name = 名前;
}
const user = 新しい User('jack');
Reflect.construct(ユーザー、['jack']);
Reflect.getPrototypeOf(obj)
Reflect.getPrototypeOf メソッドは、オブジェクトの __proto__ プロパティを読み取るために使用されます。

2.6 Reflect.setPrototypeOf(obj, newProto)

Reflect.setPrototypeOfメソッドは、ターゲット オブジェクトのprototypeを設定するために使用されます。設定が成功したかどうかを示すブール値を返します。

定数オブジェクト = {
  名前: 'ジャック',
}
Reflect.setPrototypeOf(obj, Array.prototype);
オブジェクトの長さ // 0

2.7 Reflect.apply(func, thisArg, args)

Reflect.applyメソッドFunction.prototype.apply.call(func, thisArg, args)と同等であり、 thisオブジェクトをバインドした後に指定された関数を実行するために使用されます。

定数数値 = [1,2,3,4,5];
Math.max.apply(Math, nums) の最小値と最大値を適用します。
// Reflect.apply 経由で呼び出されます const min = Reflect.apply(Math.min, Math, nums);

2.8 Reflect.defineProperty(ターゲット、プロパティキー、属性)

Reflect.definePropertyメソッドはObject.definePropertyと同等であり、オブジェクトのプロパティを定義するために使用されます。

定数obj = {};
Object.defineProperty(obj, 'プロパティ', {
  値: 0,
  書き込み可能: false
});

Reflect.defineProperty(obj, 'プロパティ', {
  値: 0,
  書き込み可能: false
});

2.9 Reflect.getOwnPropertyDescriptor(ターゲット、プロパティキー)

指定されたプロパティの説明オブジェクトを取得します。

2.10 Reflect.isExtensible (ターゲット)

現在のオブジェクトが拡張可能かどうかを示すブール値を返します。

2.11 Reflect.preventExtensions(ターゲット)

オブジェクトを拡張不可能にするために使用されます。操作が成功したかどうかを示すブール値を返します。

2.13 Reflect.ownKeys (ターゲット)

Reflect.ownKeysメソッドは、オブジェクトのすべてのプロパティを返すために使用されます。

定数オブジェクト = {
  名前: 'ジャック',
  年齢: 12歳
  ユーザー情報を取得する() {
    this.name + 'age is ' + this.age を返します。
  }
}
オブジェクト.getOwnPropertyNames(obj)
Reflect.ownKeys(obj) // ['name', 'age', 'userInfo']

3. JavaScript のプロキシ

プロキシはプログラミングに非常に役立ちます。プロキシを使用すると、ターゲット オブジェクトの前に「インターセプション」レイヤーを追加して、共通のロジックを実装できます。

プロキシコンストラクターProxy (ターゲット、 Proxy ) パラメータ:

  • target : プロキシのターゲット オブジェクト。組み込み配列、関数、プロキシ オブジェクトなど、任意のタイプのオブジェクトを指定できます。
  • handler : 特定の操作が発生したときに処理機能を提供するプロパティを持つオブジェクトです。
const ユーザー = {名前: 'hello'}
const proxy = 新しいProxy(user, {
  get: function(target, property) { // プロパティの読み取り時にトリガーされます return 'hi';
  }
});
proxy.name // 'こんにちは'

3.1 プロキシでサポートされる傍受操作

  • handler.get(target, property, receiver)
  • handler.set(target, property, value, receiver)
  • handler.has(target, property)
  • handler.defineProperty(target, property, descriptor)
  • handler.deleteProperty(target, property)
  • handler.getOwnPropertyDescriptor(target, prop)
  • handler.getPrototypeOf(target)
  • handler.setPrototypeOf(target, prototype)
  • handler.isExtensible(target)
  • handler.ownKeys(target)
  • handler.preventExtensions(target)
  • handler.apply(target, thisArg, argumentsList)
  • handler.construct(target, argumentsList, newTarget)

3.2 取得()

これは、特定の属性の読み取り操作をインターセプトするために使用されます。ターゲット オブジェクト、属性名、 proxyインスタンス自体の 3 つのパラメータを受け入れることができます。最後のパラメータはオプションです。

定数ユーザー = {
  名前: 'ジャック'
}
// 属性が存在する場合にのみ値を返し、存在しない場合は例外をスローします。
const proxy = 新しいProxy(user, {
  取得: 関数(ターゲット、プロパティ) {
    if (!(ターゲット内のプロパティ)) {
       新しい ReferenceError をスローします (`${property} は存在しません。`);
    }
    ターゲット[プロパティ]を返します。
  }
});
proxy.name // ジャック
proxy.age // ReferenceError: age が存在しません。


いくつかの共通プロキシ オブジェクトを定義し、子オブジェクトにそれらを継承させることができます。

// 属性が存在する場合にのみ値を返し、存在しない場合は例外をスローします。
const proxy = 新しいプロキシ({}, {
  取得: 関数(ターゲット、プロパティ) {
    if (!(ターゲット内のプロパティ)) {
       新しい ReferenceError をスローします (`${property} は存在しません。`);
    }
    ターゲット[プロパティ]を返します。
  }
});
obj = Object.create(プロキシ);
obj.name = 'こんにちは'
obj.name // こんにちは
obj.age // ReferenceError: age が存在しません。

3.3 設定()

これは、特定の属性の割り当て操作をインターセプトするために使用されます。ターゲット オブジェクト、属性名、属性値、およびProxyインスタンス自体の 4 つのパラメータを受け入れることができます。最後のパラメータはオプションです。

// 文字タイプ属性の長さチェック let sizeValidator = {
  設定: 関数(ターゲット、プロパティ、値、レシーバー) {
    if (typeof value == 'string' && value.length > 5) {
       throw new RangeError('5文字を超えることはできません。');
    }
    ターゲット[プロパティ] = 値;
    true を返します。
  }
};

const バリデーター = 新しいプロキシー({}、sizeValidator);
obj = Object.create(validator); を作成します。
obj.name = '123456' // RangeError: 5 文字を超えることはできません。
obj.age = 12 // 12

3.4 は()

HasProperty操作をインターセプトするために使用されます。つまり、このメソッドは、オブジェクトに特定のプロパティがあるかどうかを判断するときに有効になります。 in演算子など。

ターゲット オブジェクトとクエリする属性名の 2 つのパラメータを受け入れます。

定数ハンドラ = {
  (ターゲット、キー) {
    キー[0] === '_'の場合{
      false を返します。
    }
    ターゲットのキーを返します。
  }
};
var target = { _prop: 'foo', prop: 'foo' };
var proxy = 新しい Proxy(ターゲット、ハンドラー);
プロキシ内の '_prop' // false

3.5 プロパティの定義()

defineProperty()メソッドはObject.defineProperty()操作をインターセプトします。

3.6 プロパティの削除()

delete操作をインターセプトするために使用されます。このメソッドがエラーをスローするかfalseを返す場合、現在の属性はdeleteコマンドでは削除できません。

3.7 getOwnPropertyDescriptor()

getOwnPropertyDescriptor()メソッドはObject.getOwnPropertyDescriptor()をインターセプトし、プロパティ記述オブジェクトまたはundefinedを返します。

3.8 getPrototypeOf()

主にオブジェクトのプロトタイプを傍受して取得するために使用されます。傍受操作は次のとおりです。

  • Object.getPrototypeOf()
  • Reflect.getPrototypeOf()
  • __proto__
  • Object.prototype.isPrototypeOf()
  • instanceof
定数obj = {};
const プロトコル = {};
定数ハンドラ = {
    getPrototypeOf(ターゲット) {
        console.log(target === obj); // 真
        console.log(this === ハンドラー); // true
        proto を返します。
    }
};

const p = 新しいプロキシ(obj、ハンドラ);
console.log(Object.getPrototypeOf(p) === proto); // 真

3.9 プロトタイプの設定()

主にObject.setPrototypeOf()メソッドをインターセプトするために使用されます。

const ハンドラReturnsFalse = {
    setPrototypeOf(ターゲット、新しいProto) {
        false を返します。
    }
};

const newProto = {}、ターゲット = {};

const p1 = 新しいプロキシ(ターゲット、ハンドラーはFalseを返します);
Object.setPrototypeOf(p1, newProto); // TypeError をスローします
Reflect.setPrototypeOf(p1, newProto); // false を返す

3.10 拡張可能()

メソッドは Object.isExtensible() 操作をインターセプトします。

const p = 新しいプロキシ({}, {
  isExtensible: 関数(ターゲット) {
    console.log('呼び出されました');
    return true; //true の値を表すために return 1; などを使用することもできます}
});

console.log(Object.isExtensible(p)); // "呼び出されました"
                                     // 真実

3.11 所有キー()

オブジェクト自身の属性の読み取り操作をインターセプトするために使用されます。具体的には、以下の操作が傍受されます。

  • Object.getOwnPropertyNames()
  • Object.getOwnPropertySymbols()
  • Object.keys()
  • for...inループ。
const p = 新しいプロキシ({}, {
  所有キー: 関数(ターゲット) {
    console.log('呼び出されました');
    ['a', 'b', 'c'] を返します。
  }
});

console.log(Object.getOwnPropertyNames(p)); // "呼び出されました"

3.12 拡張機能の防止()

Object.preventExtensions()をインターセプトするために使用されます。このメソッドはブール値を返す必要があります。そうでない場合は、自動的にブール値に変換されます。

このメソッドには制限があります。proxy.preventExtensions proxy.preventExtensions 、ターゲット オブジェクトが拡張可能でない (つまり、 Object.isExtensible(proxy)為false ) 場合にのみtrueを返すことができ、それ以外の場合はエラーが報告されます。

const p = 新しいプロキシ({}, {
  拡張機能を防止: 関数(ターゲット) {
    console.log('呼び出されました');
    オブジェクト.preventExtensions(ターゲット);
    true を返します。
  }
});

console.log(Object.preventExtensions(p)); // "呼び出されました"
                                          // 間違い

3.13 適用()

apply メソッドは次の操作をインターセプトします。

  • proxy(...args)
  • Function.prototype.apply()Function.prototype.call()
  • Reflect.apply()

ターゲット オブジェクト、ターゲット オブジェクトのコンテキスト オブジェクト (this)、およびターゲット オブジェクトのパラメーター配列の 3 つのパラメーターを受け入れます。

定数ハンドラ = {
  適用 (ターゲット、ctx、引数) {
    Reflect.apply(...引数) を返します。
  }
};


例:

定数ターゲット = 関数 () { };
定数ハンドラ = {
  適用: 関数 (ターゲット、thisArg、argumentsList) {
    console.log('呼び出されました: ' + argumentsList.join(', '));
    引数リスト[0] + 引数リスト[1] + 引数リスト[2]を返します。
  }
};

const p = 新しいプロキシ(ターゲット、ハンドラー);
p(1,2,3) // "呼び出された: 1, 2, 3" 6

3.14 コンストラクト()

newコマンドをインターセプトするために使用されます。インターセプト オブジェクトの記述方法は次のとおりです。

定数ハンドラ = {
  構築 (ターゲット、引数、新しいターゲット) {
    新しいターゲット(...args)を返します。
  }
};

このメソッドは 3 つのパラメータを受け入れます。

  • target : ターゲットオブジェクト。
  • args : コンストラクターへの引数の配列。
  • newTarget : インスタンス オブジェクトを作成するときに、新しいコマンドが作用するコンストラクター。

注意:メソッドはオブジェクトを返す必要があり、ターゲット オブジェクトは関数である必要があります。そうでない場合はエラーが報告されます。

const p = 新しいプロキシ(関数() {}, {
  構成: function(target, argumentsList) {
    0を返します。
  }
});

new p() // 戻り値がオブジェクトではないため、エラーが報告されます const p = new Proxy({}, {
  構成: function(target, argumentsList) {
    戻る {};
  }
});
new p() // 対象オブジェクトは関数ではないのでエラー

4. オブザーバーパターン

オブザーバーは非常に一般的なパターンであり、オブジェクトの状態が変化すると、それに依存するすべてのオブジェクトに通知され、自動的に更新されるものとして定義されます。

監視対象オブジェクトの状態が変化したときに監視関数が自動的に実行される例をProxyを使用して実装します。

オブザーバー関数は、観測対象をラップし、観測機能を追加します。

  • observable監視対象をラップし、 Proxyオブジェクトを返します。
  • observeキューに観察機能を追加します。
const queuedObservers = 新しい Set();

const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});
// プロパティが変更されたときに自動的に監視関数を実行します。
関数set(ターゲット、キー、値、レシーバー) {
  const result = Reflect.set(ターゲット、キー、値、レシーバー);
  キューに入れられたオブザーバー。各オブザーバーをそれぞれ処理します。
  結果を返します。
}

例:

const ユーザー = 観測可能({
  名前: 'ジャック',
  年齢: 20
});

関数userInfo() {
  console.log(`${user.name}, ${user.age}`)
}

観察(ユーザー情報);
user.name = 'Xiaoming'; // Xiaoming、20歳

これで、フロントエンドJavaScriptリフレクションとプロキシに関するこの記事は終了です。JavaScript のリフレクションとプロキシに関するより関連性の高いコンテンツについては、123WORDPRESS.COM で以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • JavaScript でのプロキシの使用を理解するための記事
  • JavaScript デザインパターン プロキシパターンの学習
  • JavaScript デザインパターン - プロキシパターンの原則と使用例の分析
  • JavaScriptはイベントリスナーをイベント委任にバッチで追加します。詳細なプロセス
  • JavaScriptイベント実行メカニズムの深い理解
  • js におけるイベントバブリングとイベントキャプチャの簡単な分析
  • JavaScript イベント ループのケース スタディ
  • JavaScript データ プロキシとイベントの詳細な分析

<<:  MySQL主キー命名戦略関連

>>:  CSS の歪んだ影の実装コード

推薦する

SNMP4J サーバー接続タイムアウト問題の解決策

弊社のネットワーク管理センターは管理センター兼サーバーとして機能します!各管理対象デバイスは、TCP...

CSS3の新しいセレクタの例

構造(位置)擬似クラスセレクタ(CSS3) :first-child : 指定されたセレクタは、親要...

Linux コマンドラインでパケットをキャプチャするために tcpdump を使用するいくつかの機能

tcpdump は、ネットワークの問題のトラブルシューティングに効果的に役立つ、柔軟で強力なパケット...

Dockerはクロスプラットフォーム機能を実現するためにnet5プログラムを導入

展開環境: ここでは docker コンテナ、Linux システム、VmWare 仮想マシンが使用さ...

衝突検出を実装するためのjs

この記事の例では、衝突検出を実装するためのjsの具体的なコードを参考までに共有しています。具体的な内...

Vueのコンポーネントの詳細な説明

目次1. コンポーネントの登録2. コンポーネントの使用3. 父から息子へ4. 息子から父へ5. ス...

タブステータスバーの切り替え効果を実現するための js と jQuery

今日は、タブ バーをクリックして切り替えるという目的を実現するために、js と jQuery を使用...

docker と docker-compose による eureka の高可用性の実現の詳細な説明

最近、新しいプロジェクトでは springcloud と docker が使用されています。この 2...

td セルを結合した場合の td 幅の問題

以下の例では、名前が入っている td の幅が 60px のとき、2 行目の文字数が少ない場合は正常に...

Linux で文字化けしたファイルや特殊文字のファイルを削除する方法

エンコーディングの理由により、Linux サーバーに中国語のファイルやディレクトリをアップロードまた...

Dockerコンテナのタイムゾーン調整操作

Docker コンテナのタイムゾーンがホストマシンと一致しているかどうかを確認するにはどうすればよい...

画像ソーシャルネットワーキングサイトのUIアプリケーションの比較分析(図)

私たちの生活、仕事、勉強において、ソーシャル ネットワークは徐々に将来のインターネット発展のトレンド...

組み込み Linux で QT アプリケーションを再起動する簡単な方法 (QT4.8 qws ベース)

アプリケーション ソフトウェアには通常、次のようなビジネス要件があります。新しいバージョンの APP...

MySQL InnoDB ロック メカニズムの詳細な例

1. InnoDBのロック機構InnoDB ストレージ エンジンは、行レベルのロックとトランザクショ...

HTML テーブル マークアップ チュートリアル (28): セルの境界線の色属性 BORDERCOLOR

テーブルを美しくするために、セルごとに異なる境界線の色を設定できます。基本的な構文<TD 境界...