Skip to content

Commit 98b69e1

Browse files
committed
Update 1-js/99-js-misc/01-proxy/article.md
1 parent 3104013 commit 98b69e1

File tree

1 file changed

+28
-24
lines changed

1 file changed

+28
-24
lines changed

1-js/99-js-misc/01-proxy/article.md

+28-24
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@
22

33
`Proxy` オブジェクトは別のオブジェクトをラップし、プロパティやその他の読み取り/書き込みなどの操作をインターセプトします。必要に応じてそれらを独自に処理したり、オブジェクトが透過的にそれらを処理できるようにします。
44

5-
Proxy は多くのライブラリや一部のブラウザフレームワークで使われています。このチャプターでは、多くの実践的なアプリケーションを紹介します。
5+
Proxy は多くのライブラリや一部のブラウザフレームワークで使われています。この章では、多くの実践的なアプリケーションを紹介します。
6+
7+
## Proxy
68

79
構文:
810

911
```js
1012
let proxy = new Proxy(target, handler)
1113
```
1214

13-
- `target` -- ラップするオブジェクトです。関数を含め何でもOKです
14-
- `handler` -- プロキシ設定: 操作をインターセプトするメソッド "traps" をもつオブジェクトです。例: `get` トラップは `target` のプロパティの読み取り用、`set` トラップは、`target` へのプロパティ書き込み用、など。
15+
- `target` -- ラップするオブジェクトです。関数含め何でもOKです
16+
- `handler` -- プロキシ設定: 操作をインターセプトするメソッドである "トラップ" をもつオブジェクトです。例: `get` トラップは `target` のプロパティの読み取り用、`set` トラップは、`target` へのプロパティ書き込み用、など。
1517

1618
`proxy` の操作では、`handler` に対応するトラップがある場合はそれが実行されます。それ以外の場合は、操作は `target` で実行されます。
1719

@@ -29,27 +31,27 @@ alert(proxy.test); // 5, proxy からの読み取ることができます (2)
2931
for(let key in proxy) alert(key); // test, イテレーションも機能します (3)
3032
```
3133

32-
traps がないので`proxy` 上のすべての操作は `target` に転送されます。
34+
トラップがないので`proxy` 上のすべての操作は `target` に転送されます。
3335

3436
1. 書き込み操作 `proxy.test=``target` に値を設定します。
3537
2. 読み込み操作 `proxy.test``target` からの値を返します。
3638
3. `proxy` のイテレートは、`target` からの値を返します。
3739

38-
ご覧の通り、traps がない場合は `proxy``target` に対する透過的なラッパーです。
40+
ご覧の通り、トラップがない場合は `proxy``target` に対する透過的なラッパーです。
3941

4042
![](proxy.svg)
4143

4244
`Proxy` は特別な "エキゾチックオブジェクト(exotic object)" です。`Proxy` は独自のプロパティは持っていません。空の `handler` の場合は、透過的に `target` へ操作を転送します。
4345

44-
さらに機能を有効にするために、traps を追加しましょう
46+
さらに機能を有効にするために、トラップを追加しましょう
4547

4648
これによって、何がインターセプトできるでしょう?
4749

4850
オブジェクトに対するほとんどの操作に対しては、JavaScript の仕様で いわゆる "内部メソッド" と呼ばれるものがあり、仕様ではそれらがどのように動作するかを最も低レベルで説明しています。例えば、 `[[Get]]` は、プロパティを読み取るための内部メソッドで、`[[Set]]` はプロパティを書き込むための内部メソッド、などです。これらのメソッドは仕様でのみ使用されており、名前を使ってそれらを直接使用することはできません。
4951

5052
プロキシのトラップはこれらのメソッドの呼び出しをインターセプトします。これらのメソッドは[Proxy specification](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots) 及び以下の表にリストされています。
5153

52-
内部メソッドと trap(操作をインターセプトするために `new Proxy``handler` パラメータに追加するメソッド名)の対応表です。
54+
このテーブルに、すべての内部ソッドに対するトラップがあります: 操作をインターセプトするために `new Proxy``handler` パラメータに追加できるメソッド名です:
5355

5456
| 内部メソッド | ハンドラメソッド | いつ発生するか |
5557
|-----------------|----------------|-------------|
@@ -59,16 +61,16 @@ traps がないので、`proxy` 上のすべての操作は `target` に転送
5961
| `[[Delete]]` | `deleteProperty` | `delete` 演算子 |
6062
| `[[Call]]` | `apply` | 関数呼び出し |
6163
| `[[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` |
6971

7072
```warn header="Invariants"
71-
JavaScript にはいくつかの不変条件(内部メソッドと traps によって満たされるべき条件)があります。
73+
JavaScript にはいくつかの不変条件(内部メソッドと トラップによって満たされるべき条件)があります。
7274
7375
そのほとんどは戻り値に関してです:
7476
- `[[Set]]` は値が正常に書き込まれた場合には `true` を、そうでなければ `false` を返す必要があります。
@@ -80,8 +82,7 @@ JavaScript にはいくつかの不変条件(内部メソッドと traps によ
8082
8183
traps はこれらの操作をインターセプトできますが、これらのルールには従う必要があります。
8284
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)にありますが、変なことをしない限りは違反することはないでしょう。
8586
```
8687

8788
実際の例でそれがどのように動作するのかを見てみましょう。
@@ -90,7 +91,7 @@ traps はこれらの操作をインターセプトできますが、これら
9091

9192
最も一般的なトラップ(traps)はプロパティの読み書きです。
9293

93-
読み取りをインターセプトするには、`handler` に `get(target, property, receiver)` が必要です。
94+
読み取りをインターセプトするには、`handler``get(target, property, receiver)` が必要です。
9495

9596
これはプロパティが読み取られたとき、以下の引数で実行されます。:
9697

@@ -293,8 +294,7 @@ user = new Proxy(user, {
293294
alert( Object.keys(user) ); // <empty>
294295
```
295296

296-
なぜでしょう?理由は簡単です。: `Object.keys``enumerable` フラグを持つプロパティだけを返すからです。それを確かめるため、すべてのメソッドに対し内部メソッド `[[GetOwnProperty]]` を呼び出し,
297-
[ディスクリプタ](info:property-descriptors) を取得します。すると、ここではプロパティがないので、そのディスクリプタは空であり、`enumerable` フラグがありません。そのため、スキップされます。
297+
なぜでしょう?理由は簡単です。: `Object.keys``enumerable` フラグを持つプロパティだけを返すからです。それを確かめるため、すべてのメソッドに対し内部メソッド `[[GetOwnProperty]]` を呼び出し,[ディスクリプタ](info:property-descriptors) を取得します。すると、ここではプロパティがないので、そのディスクリプタは空であり、`enumerable` フラグがありません。そのため、スキップされます。
298298

299299
`Object.keys` がプロパティを返すには、`enumerable` 付きでオブジェクトに存在するか、`[[GetOwnProperty]]`(トラップは `getOwnPropertyDescriptor`)の呼び出しをインターセプトし、`enumerable: true` を持つディスクリプタを返します。
300300

@@ -436,6 +436,7 @@ user = {
436436
}
437437
```
438438

439+
439440
`user.checkPassword()` の呼び出しはプロキシされた `user``this` (ドットの前のオブジェクトが `this` になります)として取得するため、`this._password` へのアクセスを試みると `get` トラップが機能(これはあらゆるプロパティ読み取りでトリガーされます)し、エラーをスローします。
440441

441442
そのため、`(*)` の通りオブジェクトメソッドのコンテキストを元のオブジェクトである `target` でバインドします。以降、その呼び出しでは `this` としてトラップのない `target` を使用します。
@@ -776,6 +777,7 @@ get(target, prop, receiver) {
776777
}
777778
```
778779

780+
779781
`Reflect` 呼び出しはトラップとまったく同じ名前が付けられており、同じ引数を受け付けます。特別にそのように設計されました。
780782

781783
したがって、`return Reflect...` は安全かつ考えるまでもない分かりやすい手段で操作を転送することができます。
@@ -961,9 +963,13 @@ revoke();
961963
alert(proxy.data); // Error
962964
```
963965

964-
`revoke()` 呼び出しは、プロキシからターゲットオブジェクトへのすべての内部参照を削除します。これにより繋がりがなくなります。ターゲットオブジェクトはその後ガベージコレクトできます。
966+
`revoke()` 呼び出しは、プロキシからターゲットオブジェクトへのすべての内部参照を削除します。これにより繋がりがなくなります。
965967

966-
また、プロキシオブジェクトを簡単に見つけられるよう、`WeakMap``revoke` を保持することもできます。:
968+
初期状態で、`revoke``proxy` とは別なので、現在のスコープに `revoke` を残したまま、`proxy` を渡すことが可能です。
969+
970+
`proxy.revoke = revoke` と設定することで、proxy に `revoke` メソッドをバインドすることもできます。
971+
972+
別の選択肢は、`WeakMap` を作成し、キーとして `proxy` を、値として対応する `revoke` をもたせることです。これで、簡単に proxy に対する `revoke` を見つけることができます。
967973

968974
```js run
969975
*!*
@@ -985,8 +991,6 @@ revoke();
985991
alert(proxy.data); // Error (revoked)
986992
```
987993

988-
このようなアプローチの利点は `revoke` を持って回る必要がないことです。必要なときに `proxy` を使って map から取得できます。
989-
990994
ここで `Map` の代わりに `WeakMap` を使用しているのは、ガベージコレクションをブロックしないようにするためです。proxy オブジェクトが "到達不可能" になった(e.g それを参照する変数がなくなった)場合、`WeakMap` を利用すると、不要になった `revoke` を一緒にメモリ上から削除することができます。
991995

992996
## リファレンス

0 commit comments

Comments
 (0)