Repository navigation
Expand file tree
/
Copy pathindex.md
More file actions
282 lines (245 loc) · 10.5 KB
/
Copy pathindex.md
File metadata and controls
282 lines (245 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
---
title: メタプログラミング
slug: Web/JavaScript/Guide/Meta_programming
l10n:
sourceCommit: fad67be4431d8e6c2a89ac880735233aa76c41d4
---
{{jsxref("Proxy")}} および {{jsxref("Reflect")}} オブジェクトにより、基本的な言語操作 (例えば、プロパティ参照、代入、列挙、関数呼び出しなど) に割り込み、動作をカスタマイズすることができます。この 2 つのオブジェクトのおかげで、 JavaScript でメタレベルのプログラミングが行えます。
## プロキシー
{{jsxref("Proxy")}} オブジェクトによって、特定の操作に割り込んで動作をカスタマイズすることができます。
例えば、オブジェクトのプロパティを取得してみましょう。
```js
const handler = {
get(target, name) {
return name in target ? target[name] : 42;
},
};
const p = new Proxy({}, handler);
p.a = 1;
console.log(p.a, p.b); // 1, 42
```
この `Proxy` オブジェクトは `target` (ここでは空オブジェクト) と `handler` オブジェクトを定義し、その中に `get` トラップが実装されています。ここで、プロキシーとなったオブジェクトは未定義のプロパティを取得しようとした時に `undefined` を返さず、代わりに数値 `42` を返します。
それ以外の例は {{jsxref("Proxy")}} のリファレンスページを参照してください。
### 用語集
プロキシーの機能について話題にする際は、次の用語が使用されます。
- {{jsxref("Proxy/Proxy","ハンドラー","",1)}} (handler)
- : トラップを入れるためのプレースホルダ用オブジェクト。
- トラップ (trap)
- : プロパティへのアクセスを提供するメソッドです。 (オペレーティングシステムにおけるトラップの概念と同じようなものです。)
- ターゲット (target)
- : プロキシーが仮想化するオブジェクトです。多くの場合、プロキシーのストレージバックエンドとして使用されます。拡張や設定できないオブジェクトのプロパティの不変条件(変更されない意味)がターゲットに対して検証されます。
- {{Glossary("invariant", "不変条件")}} (invariant)
- : 独自の操作を実装した際に変更されない意味を**不変条件**と呼びます。ハンドラーの不変条件に違反した場合、 {{jsxref("TypeError")}} が発生します。
## ハンドラーとトラップ
次の表は、 `Proxy` オブジェクトに対して利用可能なトラップをまとめたものです。詳細な説明と例については、[リファレンスページ](/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy)を参照してください。
<table class="standard-table">
<thead>
<tr>
<th>ハンドラー / トラップ</th>
<th>割り込みされる処理</th>
</tr>
</thead>
<tbody>
<tr>
<td>
{{jsxref("Proxy/Proxy/getPrototypeOf", "handler.getPrototypeOf()")}}
</td>
<td>
{{jsxref("Object.getPrototypeOf()")}}<br />{{jsxref("Reflect.getPrototypeOf()")}}<br />{{jsxref("Object/proto", "__proto__")}}<br />{{jsxref("Object.prototype.isPrototypeOf()")}}<br />{{jsxref("instanceof")}}
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/setPrototypeOf", "handler.setPrototypeOf()")}}
</td>
<td>
{{jsxref("Object.setPrototypeOf()")}}<br />{{jsxref("Reflect.setPrototypeOf()")}}
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/isExtensible", "handler.isExtensible()")}}
</td>
<td>
{{jsxref("Object.isExtensible()")}}<br />{{jsxref("Reflect.isExtensible()")}}
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/preventExtensions", "handler.preventExtensions()")}}
</td>
<td>
{{jsxref("Object.preventExtensions()")}}<br />{{jsxref("Reflect.preventExtensions()")}}
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/getOwnPropertyDescriptor", "handler.getOwnPropertyDescriptor()")}}
</td>
<td>
{{jsxref("Object.getOwnPropertyDescriptor()")}}<br />{{jsxref("Reflect.getOwnPropertyDescriptor()")}}
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/defineProperty", "handler.defineProperty()")}}
</td>
<td>
{{jsxref("Object.defineProperty()")}}<br />{{jsxref("Reflect.defineProperty()")}}
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/has", "handler.has()")}}
</td>
<td>
<dl>
<dt>プロパティの照会</dt>
<dd><code>foo in proxy</code></dd>
<dt>継承されたプロパティの照会</dt>
<dd>
<code>foo in Object.create(<var>proxy</var>)</code
><br />{{jsxref("Reflect.has()")}}
</dd>
</dl>
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/get", "handler.get()")}}
</td>
<td>
<dl>
<dt>プロパティへのアクセス</dt>
<dd>
<code><var>proxy</var>[foo]</code><br /><code
><var>proxy</var>.bar</code
>
</dd>
<dt>継承されたプロパティへのアクセス</dt>
<dd>
<!-- markdownlint-disable MD011 -->
<code>Object.create(<var>proxy</var>)[foo]</code
><br />{{jsxref("Reflect.get()")}}
</dd>
</dl>
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/set", "handler.set()")}}
</td>
<td>
<dl>
<dt>プロパティへの代入</dt>
<dd>
<code><var>proxy</var>[foo] = bar</code><br /><code
><var>proxy</var>.foo = bar</code
>
</dd>
<dt>継承されたプロパティへの代入</dt>
<dd>
<code>Object.create(<var>proxy</var>)[foo] = bar</code
><br />{{jsxref("Reflect.set()")}}
</dd>
<!-- markdownlint-enable MD011 -->
</dl>
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/deleteProperty", "handler.deleteProperty()")}}
</td>
<td>
<dl>
<dt>プロパティの削除</dt>
<dd>
<code>delete <var>proxy</var>[foo]</code><br /><code
>delete <var>proxy</var>.foo</code
><br />{{jsxref("Reflect.deleteProperty()")}}
</dd>
</dl>
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/ownKeys", "handler.ownKeys()")}}
</td>
<td>
{{jsxref("Object.getOwnPropertyNames()")}}<br />{{jsxref("Object.getOwnPropertySymbols()")}}<br />{{jsxref("Object.keys()")}}<br />{{jsxref("Reflect.ownKeys()")}}
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/apply", "handler.apply()")}}
</td>
<td>
<code>proxy(..args)</code
><br />{{jsxref("Function.prototype.apply()")}} および
{{jsxref("Function.prototype.call()")}}<br />{{jsxref("Reflect.apply()")}}
</td>
</tr>
<tr>
<td>
{{jsxref("Proxy/Proxy/construct", "handler.construct()")}}
</td>
<td>
<code>new proxy(...args)</code
><br />{{jsxref("Reflect.construct()")}}
</td>
</tr>
</tbody>
</table>
## 取り消し可能 `Proxy`
{{jsxref("Proxy.revocable()")}} メソッドは取り消し可能な `Proxy` オブジェクトの生成に使用されます。これにより、プロキシーを `revoke` 関数で取り消し、プロキシーの機能を停止することができます。
その後はプロキシーを通じたいかなる操作も {{jsxref("TypeError")}} になります。
```js
const revocable = Proxy.revocable(
{},
{
get(target, name) {
return `[[${name}]]`;
},
},
);
const proxy = revocable.proxy;
console.log(proxy.foo); // "[[foo]]"
revocable.revoke();
console.log(proxy.foo); // TypeError: Cannot perform 'get' on a proxy that has been revoked
proxy.foo = 1; // TypeError: Cannot perform 'set' on a proxy that has been revoked
delete proxy.foo; // TypeError: Cannot perform 'deleteProperty' on a proxy that has been revoked
console.log(typeof proxy); // "object" が返され, typeof はどんなトラップも引き起こさない
```
## リフレクション
{{jsxref("Reflect")}} は JavaScript で割り込み操作を行うメソッドを提供する組み込みオブジェクトです。そのメソッドは[プロキシーのハンドラー](/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy)のメソッドと同じです。
`Reflect` は関数オブジェクトではありません。
`Reflect` はハンドラーから`ターゲット`への既定の操作を転送するのに役立ちます。
例えば、{{jsxref("Reflect.has()")}} を使えば、 [`in` 演算子](/ja/docs/Web/JavaScript/Reference/Operators/in)を関数として使うことができます。
```js
Reflect.has(Object, "assign"); // true
```
### より優れた apply() 関数
`Reflect` が登場する前は、所定の `this` 値と配列や[配列風オブジェクト](/ja/docs/Web/JavaScript/Guide/Indexed_collections#配列風オブジェクトの扱い)として提供される `arguments` を使って関数を呼び出す {{jsxref("Function.prototype.apply()")}} メソッドがよく使われてきました。
```js
Function.prototype.apply.call(Math.floor, undefined, [1.75]);
```
{{jsxref("Reflect.apply")}} を使えば、より簡潔で分かりやすいものにできます。
```js
Reflect.apply(Math.floor, undefined, [1.75]);
// 1
Reflect.apply(String.fromCharCode, undefined, [104, 101, 108, 108, 111]);
// "hello"
Reflect.apply(RegExp.prototype.exec, /ab/, ["confabulation"]).index;
// 4
Reflect.apply("".charAt, "ponies", [3]);
// "i"
```
### プロパティ定義の成否チェック
{{jsxref("Object.defineProperty")}} は成功すればオブジェクトを返し、そうでなければ {{jsxref("TypeError")}} が発生するので、 {{jsxref("Statements/try...catch", "try...catch")}} ブロックを使って、プロパティの定義中に発生したエラーを捕捉します。{{jsxref("Reflect.defineProperty()")}} は成功のステータスを論理値で返すので、ここでは {{jsxref("Statements/if...else","if...else")}} ブロックを使うだけでよいのです。
```js
if (Reflect.defineProperty(target, property, attributes)) {
// 成功した時の処理
} else {
// 失敗した時の処理
}
```