2
2
3
3
` Proxy ` オブジェクトは別のオブジェクトをラップし、プロパティやその他の読み取り/書き込みなどの操作をインターセプトします。必要に応じてそれらを独自に処理したり、オブジェクトが透過的にそれらを処理できるようにします。
4
4
5
- Proxy は多くのライブラリや一部のブラウザフレームワークで使われています。このチャプターでは、多くの実践的なアプリケーションを紹介します。
5
+ Proxy は多くのライブラリや一部のブラウザフレームワークで使われています。この章では、多くの実践的なアプリケーションを紹介します。
6
+
7
+ ## Proxy
6
8
7
9
構文:
8
10
9
11
``` js
10
12
let proxy = new Proxy (target, handler)
11
13
```
12
14
13
- - ` target ` -- ラップするオブジェクトです。関数を含め何でもOKです 。
14
- - ` handler ` -- プロキシ設定: 操作をインターセプトするメソッド "traps " をもつオブジェクトです。例: ` get ` トラップは ` target ` のプロパティの読み取り用、` set ` トラップは、` target ` へのプロパティ書き込み用、など。
15
+ - ` target ` -- ラップするオブジェクトです。関数含め何でもOKです 。
16
+ - ` handler ` -- プロキシ設定: 操作をインターセプトするメソッドである "トラップ " をもつオブジェクトです。例: ` get ` トラップは ` target ` のプロパティの読み取り用、` set ` トラップは、` target ` へのプロパティ書き込み用、など。
15
17
16
18
` proxy ` の操作では、` handler ` に対応するトラップがある場合はそれが実行されます。それ以外の場合は、操作は ` target ` で実行されます。
17
19
@@ -29,27 +31,27 @@ alert(proxy.test); // 5, proxy からの読み取ることができます (2)
29
31
for (let key in proxy) alert (key); // test, イテレーションも機能します (3)
30
32
```
31
33
32
- traps がないので 、` proxy ` 上のすべての操作は ` target ` に転送されます。
34
+ トラップがないので 、` proxy ` 上のすべての操作は ` target ` に転送されます。
33
35
34
36
1 . 書き込み操作 ` proxy.test= ` は ` target ` に値を設定します。
35
37
2 . 読み込み操作 ` proxy.test ` は ` target ` からの値を返します。
36
38
3 . ` proxy ` のイテレートは、` target ` からの値を返します。
37
39
38
- ご覧の通り、traps がない場合は ` proxy ` は ` target ` に対する透過的なラッパーです。
40
+ ご覧の通り、トラップがない場合は ` proxy ` は ` target ` に対する透過的なラッパーです。
39
41
40
42
![ ] ( proxy.svg )
41
43
42
44
` Proxy ` は特別な "エキゾチックオブジェクト(exotic object)" です。` Proxy ` は独自のプロパティは持っていません。空の ` handler ` の場合は、透過的に ` target ` へ操作を転送します。
43
45
44
- さらに機能を有効にするために、traps を追加しましょう 。
46
+ さらに機能を有効にするために、トラップを追加しましょう 。
45
47
46
48
これによって、何がインターセプトできるでしょう?
47
49
48
50
オブジェクトに対するほとんどの操作に対しては、JavaScript の仕様で いわゆる "内部メソッド" と呼ばれるものがあり、仕様ではそれらがどのように動作するかを最も低レベルで説明しています。例えば、 ` [[Get]] ` は、プロパティを読み取るための内部メソッドで、` [[Set]] ` はプロパティを書き込むための内部メソッド、などです。これらのメソッドは仕様でのみ使用されており、名前を使ってそれらを直接使用することはできません。
49
51
50
52
プロキシのトラップはこれらのメソッドの呼び出しをインターセプトします。これらのメソッドは[ Proxy specification] ( https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots ) 及び以下の表にリストされています。
51
53
52
- 内部メソッドと trap( 操作をインターセプトするために ` new Proxy ` の ` handler ` パラメータに追加するメソッド名)の対応表です。
54
+ このテーブルに、すべての内部ソッドに対するトラップがあります: 操作をインターセプトするために ` new Proxy ` の ` handler ` パラメータに追加できるメソッド名です:
53
55
54
56
| 内部メソッド | ハンドラメソッド | いつ発生するか |
55
57
| -----------------| ----------------| -------------|
@@ -59,16 +61,16 @@ traps がないので、`proxy` 上のすべての操作は `target` に転送
59
61
| ` [[Delete]] ` | ` deleteProperty ` | ` delete ` 演算子 |
60
62
| ` [[Call]] ` | ` apply ` | 関数呼び出し |
61
63
| ` [[Construct]] ` | ` construct ` | ` new ` 演算子 |
62
- | ` [[GetPrototypeOf]] ` | ` getPrototypeOf ` | [ Object.getPrototypeOf] ( https://developer.mozilla.org/en-US/docs/Web /JavaScript/Reference/Global_Objects/Object/getPrototypeOf) |
63
- | ` [[SetPrototypeOf]] ` | ` setPrototypeOf ` | [ Object.setPrototypeOf] ( https://developer.mozilla.org/en-US/docs/Web /JavaScript/Reference/Global_Objects/Object/setPrototypeOf) |
64
- | ` [[IsExtensible]] ` | ` isExtensible ` | [ Object.isExtensible] ( https://developer.mozilla.org/en-US/docs/Web /JavaScript/Reference/Global_Objects/Object/isExtensible) |
65
- | ` [[PreventExtensions]] ` | ` preventExtensions ` | [ Object.preventExtensions] ( https://developer.mozilla.org/en-US/docs/Web /JavaScript/Reference/Global_Objects/Object/preventExtensions) |
66
- | ` [[DefineOwnProperty]] ` | ` defineProperty ` | [ Object.defineProperty] ( https://developer.mozilla.org/en-US/docs/Web/ JavaScript/Reference/Global_Objects/Object/defineProperty) , [ Object.defineProperties] ( https://developer.mozilla.org/en-US/docs/Web /JavaScript/Reference/Global_Objects/Object/defineProperties) |
67
- | ` [[GetOwnProperty]] ` | ` getOwnPropertyDescriptor ` | [ Object.getOwnPropertyDescriptor] ( https://developer.mozilla.org/en-US/docs/Web /JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor) , ` for..in ` , ` Object.keys/values/entries ` |
68
- | ` [[OwnPropertyKeys]] ` | ` ownKeys ` | [ Object.getOwnPropertyNames] ( https://developer.mozilla.org/en-US/docs/Web/ JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames) , [ Object.getOwnPropertySymbols] ( https://developer.mozilla.org/en-US/docs/Web/ JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols) , ` for..in ` , ` Object/ keys/values/entries ` |
64
+ | ` [[GetPrototypeOf]] ` | ` getPrototypeOf ` | [ Object.getPrototypeOf] ( mdn: /JavaScript/Reference/Global_Objects/Object/getPrototypeOf) |
65
+ | ` [[SetPrototypeOf]] ` | ` setPrototypeOf ` | [ Object.setPrototypeOf] ( mdn: /JavaScript/Reference/Global_Objects/Object/setPrototypeOf) |
66
+ | ` [[IsExtensible]] ` | ` isExtensible ` | [ Object.isExtensible] ( mdn: /JavaScript/Reference/Global_Objects/Object/isExtensible) |
67
+ | ` [[PreventExtensions]] ` | ` preventExtensions ` | [ Object.preventExtensions] ( mdn: /JavaScript/Reference/Global_Objects/Object/preventExtensions) |
68
+ | ` [[DefineOwnProperty]] ` | ` defineProperty ` | [ Object.defineProperty] ( mdn:/ JavaScript/Reference/Global_Objects/Object/defineProperty) , [ Object.defineProperties] ( mdn: /JavaScript/Reference/Global_Objects/Object/defineProperties) |
69
+ | ` [[GetOwnProperty]] ` | ` getOwnPropertyDescriptor ` | [ Object.getOwnPropertyDescriptor] ( mdn: /JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor) , ` for..in ` , ` Object.keys/values/entries ` |
70
+ | ` [[OwnPropertyKeys]] ` | ` ownKeys ` | [ Object.getOwnPropertyNames] ( mdn:/ JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames) , [ Object.getOwnPropertySymbols] ( mdn:/ JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols) , ` for..in ` , ` Object. keys/values/entries ` |
69
71
70
72
``` warn header="Invariants"
71
- JavaScript にはいくつかの不変条件(内部メソッドと traps によって満たされるべき条件 )があります。
73
+ JavaScript にはいくつかの不変条件(内部メソッドと トラップによって満たされるべき条件 )があります。
72
74
73
75
そのほとんどは戻り値に関してです:
74
76
- `[[Set]]` は値が正常に書き込まれた場合には `true` を、そうでなければ `false` を返す必要があります。
@@ -80,8 +82,7 @@ JavaScript にはいくつかの不変条件(内部メソッドと traps によ
80
82
81
83
traps はこれらの操作をインターセプトできますが、これらのルールには従う必要があります。
82
84
83
- 不変条件は、言語機能の正しさと一貫した動作を保証するものです。完全な不変条件のリストは [仕様]
84
- (https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots)にありますが、変なことをしない限りは違反することはないでしょう。
85
+ 不変条件は、言語機能の正しさと一貫した動作を保証するものです。完全な不変条件のリストは [仕様](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots)にありますが、変なことをしない限りは違反することはないでしょう。
85
86
```
86
87
87
88
実際の例でそれがどのように動作するのかを見てみましょう。
@@ -90,7 +91,7 @@ traps はこれらの操作をインターセプトできますが、これら
90
91
91
92
最も一般的なトラップ(traps)はプロパティの読み書きです。
92
93
93
- 読み取りをインターセプトするには、` handler ` に ` get(target, property, receiver) ` が必要です。
94
+ 読み取りをインターセプトするには、` handler ` に ` get(target, property, receiver) ` が必要です。
94
95
95
96
これはプロパティが読み取られたとき、以下の引数で実行されます。:
96
97
@@ -293,8 +294,7 @@ user = new Proxy(user, {
293
294
alert ( Object .keys (user) ); // <empty>
294
295
```
295
296
296
- なぜでしょう?理由は簡単です。: ` Object.keys ` は ` enumerable ` フラグを持つプロパティだけを返すからです。それを確かめるため、すべてのメソッドに対し内部メソッド ` [[GetOwnProperty]] ` を呼び出し,
297
- [ ディスクリプタ] ( info:property-descriptors ) を取得します。すると、ここではプロパティがないので、そのディスクリプタは空であり、` enumerable ` フラグがありません。そのため、スキップされます。
297
+ なぜでしょう?理由は簡単です。: ` Object.keys ` は ` enumerable ` フラグを持つプロパティだけを返すからです。それを確かめるため、すべてのメソッドに対し内部メソッド ` [[GetOwnProperty]] ` を呼び出し,[ ディスクリプタ] ( info:property-descriptors ) を取得します。すると、ここではプロパティがないので、そのディスクリプタは空であり、` enumerable ` フラグがありません。そのため、スキップされます。
298
298
299
299
` Object.keys ` がプロパティを返すには、` enumerable ` 付きでオブジェクトに存在するか、` [[GetOwnProperty]] ` (トラップは ` getOwnPropertyDescriptor ` )の呼び出しをインターセプトし、` enumerable: true ` を持つディスクリプタを返します。
300
300
@@ -436,6 +436,7 @@ user = {
436
436
}
437
437
```
438
438
439
+
439
440
` user.checkPassword() ` の呼び出しはプロキシされた ` user ` を ` this ` (ドットの前のオブジェクトが ` this ` になります)として取得するため、` this._password ` へのアクセスを試みると ` get ` トラップが機能(これはあらゆるプロパティ読み取りでトリガーされます)し、エラーをスローします。
440
441
441
442
そのため、` (*) ` の通りオブジェクトメソッドのコンテキストを元のオブジェクトである ` target ` でバインドします。以降、その呼び出しでは ` this ` としてトラップのない ` target ` を使用します。
@@ -776,6 +777,7 @@ get(target, prop, receiver) {
776
777
}
777
778
```
778
779
780
+
779
781
` Reflect ` 呼び出しはトラップとまったく同じ名前が付けられており、同じ引数を受け付けます。特別にそのように設計されました。
780
782
781
783
したがって、` return Reflect... ` は安全かつ考えるまでもない分かりやすい手段で操作を転送することができます。
@@ -961,9 +963,13 @@ revoke();
961
963
alert (proxy .data ); // Error
962
964
```
963
965
964
- ` revoke() ` 呼び出しは、プロキシからターゲットオブジェクトへのすべての内部参照を削除します。これにより繋がりがなくなります。ターゲットオブジェクトはその後ガベージコレクトできます。
966
+ ` revoke() ` 呼び出しは、プロキシからターゲットオブジェクトへのすべての内部参照を削除します。これにより繋がりがなくなります。
965
967
966
- また、プロキシオブジェクトを簡単に見つけられるよう、` WeakMap ` に ` revoke ` を保持することもできます。:
968
+ 初期状態で、` revoke ` は ` proxy ` とは別なので、現在のスコープに ` revoke ` を残したまま、` proxy ` を渡すことが可能です。
969
+
970
+ ` proxy.revoke = revoke ` と設定することで、proxy に ` revoke ` メソッドをバインドすることもできます。
971
+
972
+ 別の選択肢は、` WeakMap ` を作成し、キーとして ` proxy ` を、値として対応する ` revoke ` をもたせることです。これで、簡単に proxy に対する ` revoke ` を見つけることができます。
967
973
968
974
``` js run
969
975
* ! *
@@ -985,8 +991,6 @@ revoke();
985
991
alert (proxy .data ); // Error (revoked)
986
992
```
987
993
988
- このようなアプローチの利点は ` revoke ` を持って回る必要がないことです。必要なときに ` proxy ` を使って map から取得できます。
989
-
990
994
ここで ` Map ` の代わりに ` WeakMap ` を使用しているのは、ガベージコレクションをブロックしないようにするためです。proxy オブジェクトが "到達不可能" になった(e.g それを参照する変数がなくなった)場合、` WeakMap ` を利用すると、不要になった ` revoke ` を一緒にメモリ上から削除することができます。
991
995
992
996
## リファレンス
0 commit comments