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
|
---
title: Meta programming
slug: Web/JavaScript/Guide/Meta_programming
tags:
- Guide
- JavaScript
- Proxy
- Reflect
- 'l10n:priority'
translation_of: Web/JavaScript/Guide/Meta_programming
---
<div>{{jsSidebar("JavaScript Guide")}} {{Previous("Web/JavaScript/Guide/Iterators_and_Generators")}}</div>
<p class="summary">Beginnend mit ECMAScript 2015 hat JavaScript Unterstützung für {{jsxref("Proxy")}} and {{jsxref("Reflect")}} Objekte erhalten, welche das Abfangen und Definieren von benutzerdefinierten Verhaltens für grundlegenden Sprachoperation erlaubt (z. B. Eigenschaftensuche, Zuweisung, Aufzählung, Funktionsaufruf usw.). Mit der Hilfe dieser beiden Objekte ist es möglich auf der Metaebene von JavaScript zu programmieren.</p>
<h2 id="Proxies">Proxies</h2>
<p>Eingeführt in ECMAScript 6 erlaubt das {{jsxref("Proxy")}} Objekt das Abfangen und Definieren von benutzerdefinierten Verhaltens für bestimmte Operationen. Zum Beispiel um die Eigenschaft eines Objektes zu erhalten:</p>
<pre class="brush: js">var handler = {
get: function(target, name) {
return name in target ? target[name] : 42;
}
};
var p = new Proxy({}, handler);
p.a = 1;
console.log(p.a, p.b); // 1, 42
</pre>
<p>Das <code>Proxy</code> Objekt definiert einen <em>Ziel</em> (<code>target</code>) (hier ein leeres Objekt) und einen <em>handler</em> (Verhaltens) Objekt in dem ein <code>get</code> <em>Trap</em> implementiert ist. In diesem Beispiel wird kein <code>undefined</code> zurückgegeben, wenn Eigenschaften nicht definiert sind. Stattdessen wird die Zahl 42 zurückgegeben.</p>
<p>Weitere Beispiele sind auf der {{jsxref("Proxy")}} Referenzseite verfügbar.</p>
<h3 id="Terminologie">Terminologie</h3>
<p>Die folgenden Terme werden im Zusammenhang mit der Funktionalität von Proxies verwendet.</p>
<dl>
<dt>{{jsxref("Global_Objects/Proxy/handler","Handler","","true")}}</dt>
<dd>Platzhalterobjekt, welches Traps enthält.</dd>
<dt>Traps</dt>
<dd>Die Methoden, die Zugriff auf Eigenschaften unterstützen. Diese sind analog zu Traps in Betriebssystemen.</dd>
<dt>Ziel</dt>
<dd>Objekt, welches vom Proxy virtualisiert wird. Es wird häufig als Speicher-Backend für den Proxy benutzt. Invarianten (Semantik, die unverändert bleiben) bezüglich nicht erweiterbarer Objekteigenschaften oder nicht konfigurierbarer Eigenschaften werden gegen das Ziel verifiziert.</dd>
<dt>Invarianten</dt>
<dd>Semantiken, die bei der Implementierung von benutzerdefinierten Operationen unverändert bleiben, werden als Invarianten bezeichnet. Wenn Sie gegen die Invarianten eines Handlers verstoßen, wird ein {{jsxref("TypeError")}} erzeugt.</dd>
</dl>
<h2 id="Handlers_und_Traps">Handlers und Traps</h2>
<p>Die Folgende Tabelle fasst die verfügbaren Traps von <code>Proxy</code> Objekten zusammen. Siehe auf der <a href="/de/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler">Referenzseite</a> für detailliertere Erklärungen und Beispiele.</p>
<table class="standard-table">
<thead>
<tr>
<th>Handler / Trap</th>
<th>Interceptions</th>
<th>Invarianten</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/getPrototypeOf", "handler.getPrototypeOf()")}}</td>
<td>{{jsxref("Object.getPrototypeOf()")}}<br>
{{jsxref("Reflect.getPrototypeOf()")}}<br>
{{jsxref("Object/proto", "__proto__")}}<br>
{{jsxref("Object.prototype.isPrototypeOf()")}}<br>
{{jsxref("Operators/instanceof", "instanceof")}}</td>
<td>
<ul>
<li>Die <code>getPrototypeOf</code> Methode muss ein Objekt or <code>null</code> zurückgeben.</li>
<li>Wenn <code>target</code> nicht erweiterbar ist, muss die <code>Object.getPrototypeOf(proxy)</code> Methode das gleiche zurückgeben wie <code>Object.getPrototypeOf(target)</code>.</li>
</ul>
</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/setPrototypeOf", "handler.setPrototypeOf()")}}</td>
<td>{{jsxref("Object.setPrototypeOf()")}}<br>
{{jsxref("Reflect.setPrototypeOf()")}}</td>
<td>Wenn <code>target</code> nicht erweiterbar ist, muss der<code> prototype</code> Parameter der gleiche Wert sein wie <code>Object.getPrototypeOf(target)</code>.</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/isExtensible", "handler.isExtensible()")}}</td>
<td>{{jsxref("Object.isExtensible()")}}<br>
{{jsxref("Reflect.isExtensible()")}}</td>
<td><code>Object.isExtensible(proxy)</code> muss den gleichen Wert wie <code>Object.isExtensible(target)</code> zurückgeben.</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/preventExtensions", "handler.preventExtensions()")}}</td>
<td>{{jsxref("Object.preventExtensions()")}}<br>
{{jsxref("Reflect.preventExtensions()")}}</td>
<td><code>Object.preventExtensions(proxy)</code> gibt nur <code>true</code> zurück, wenn <code>Object.isExtensible(proxy)</code> <code>false</code> ist.</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/getOwnPropertyDescriptor", "handler.getOwnPropertyDescriptor()")}}</td>
<td>{{jsxref("Object.getOwnPropertyDescriptor()")}}<br>
{{jsxref("Reflect.getOwnPropertyDescriptor()")}}</td>
<td>
<ul>
<li><code>getOwnPropertyDescriptor</code> muss ein Objekt oder <code>undefined</code> zurückgeben.</li>
<li>Eine Eigenschaft kann nicht als nichtexistent erkannt werden, wenn sie als nicht konfigurierbare Eigenschaft des Zielobjektes existiert.</li>
<li>Eine Eigenschaft kann nicht als nichtexistent erkannt werden, wenn sie als Eigenschaft des Zielobjektes existiert und das Zielobjekt nicht erweiterbar ist.</li>
<li>Eine Eigenschaft kann nicht als existent erkannt werden, wenn sie nicht als Eigenschaft des Zielobjektes existiert und das Zielobjekt nicht erweiterbar ist.</li>
<li>Eine Eigenschaft kann nicht als nicht konfigurierbar erkannt werden, wenn sie nicht als Eigenschaft des Zielobjektes existiert oder wenn sie als konfigurierbare Eigenschaft des Zielobjekt existiert.</li>
<li>Das Ergebnis von <code>Object.getOwnPropertyDescriptor(target)</code> kann dem Zielobjekt mit <code>Object.defineProperty</code> übergeben werden ohne, dass ein Fehler erzeugt wird.</li>
</ul>
</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/defineProperty", "handler.defineProperty()")}}</td>
<td>{{jsxref("Object.defineProperty()")}}<br>
{{jsxref("Reflect.defineProperty()")}}</td>
<td>
<ul>
<li>Eine Eigenschaft kann nicht hinzugefügt werden, wenn das Zielobjekt nicht erweiterbar ist.</li>
<li>Eine Eigenschaft kann nicht hinzugefügt werden oder zu nicht konfigurierbar geändert werden, wenn es nicht als nicht konfigurierbares Eigenschaft im Zielobjekt existiert.</li>
<li>Eine Eigenschaft darf nicht nicht konfigurierbar sein, wenn die zugehörige konfigurierbare Eigenschaft im Zielobjekt existiert.</li>
<li>Wenn eine Eigenschaft eine zugehörige Eigenschaft im Zielobjekt hat, so wird <code>Object.defineProperty(target, prop, descriptor)</code> keinen Fehler erzeugen.</li>
<li>Im Strict Mode, wird ein falscher Rückgabewert des <code>defineProperty</code> Handler einen {{jsxref("TypeError")}} Erzeugen.</li>
</ul>
</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/has", "handler.has()")}}</td>
<td>Eigenschaftsabfrage: <code>foo in proxy</code><br>
Vererbte Eigenschaftsabfrage: <code>foo in Object.create(proxy)</code><br>
{{jsxref("Reflect.has()")}}</td>
<td>
<ul>
<li>Eine Eigenschaft kan nicht als nichtexistent erkannt werden, wenn sie als nicht konfigurierbare Eigenschaft im Zielobjekt existiert.</li>
<li>Eine Eigenschaft kan nicht als nichtexistent erkannt werden, wenn sie als Eigenschaft im Zielobjekt existiert und das Zielobjekt nicht erweiterbar ist.</li>
</ul>
</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/get", "handler.get()")}}</td>
<td>Eigenschaftszugriff: <code>proxy[foo]</code>and <code>proxy.bar</code><br>
Vererbter Eigenschaftszugriff: <code>Object.create(proxy)[foo]</code><br>
{{jsxref("Reflect.get()")}}</td>
<td>
<ul>
<li>Der Wert, der für eine Eigenschaft zurückgegeben wird, muss der gleiche sein wie der in der zugehörigen Eigenschaft des Zielobjekts, wenn die Eigenschaft im Zielobjekt nicht überschreibbar und nicht konfigurierbar ist.</li>
<li>Der Wert, der für eine Eigenschaft zurückgegeben wird, muss undefined sein, wenn die zugehörige Eigenschaft im Zielobjekt einen nicht konfigurierbare Zugriffseigenschaft hat, dessen [[Get]] Attribut undefined ist.</li>
</ul>
</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/set", "handler.set()")}}</td>
<td>Eigenschaftszuweisung: <code>proxy[foo] = bar</code> and <code>proxy.foo = bar</code><br>
Vererbte Eigenschaftszuweisung: <code>Object.create(proxy)[foo] = bar</code><br>
{{jsxref("Reflect.set()")}}</td>
<td>
<ul>
<li>Der Wert kann nicht zu einem geändert werden, der anders als dem Wert im Zielobjekt ist, wenn die zugehörige Eigenschaft im Zielobjekt eine nicht überschreibbare, nicht konfigurierbare Dateneigenschaft ist.</li>
<li>Der Wert der Eigenschaft kann nicht geändert werden, wenn die zugehörige Eigenschaft im Zielobjekt nicht konfigurierbar ist und das [[Set]] Attribut den Wert <code>undefined</code> hat.</li>
<li>Im Strict Mode, wird ein falscher Rückgabewert des <code>set</code> Handlers einen {{jsxref("TypeError")}} erzeugen.</li>
</ul>
</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/deleteProperty", "handler.deleteProperty()")}}</td>
<td>Eigenschaft löschen: <code>delete proxy[foo]</code> und <code>delete proxy.foo</code><br>
{{jsxref("Reflect.deleteProperty()")}}</td>
<td>Eine Eigenschaft kann nicht gelöscht werden, Wenn sie als nicht konfigurierbare Eigenschaft im Zielobjekt existiert.</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/enumerate", "handler.enumerate()")}}</td>
<td>Eigenschaft aufzählen (enumeration) / for...in: <code>for (var name in proxy) {...}</code><br>
{{jsxref("Reflect.enumerate()")}}</td>
<td>Die <code>enumerate</code> Methode muss ein Objekt zurückgeben.</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/ownKeys", "handler.ownKeys()")}}</td>
<td>{{jsxref("Object.getOwnPropertyNames()")}}<br>
{{jsxref("Object.getOwnPropertySymbols()")}}<br>
{{jsxref("Object.keys()")}}<br>
{{jsxref("Reflect.ownKeys()")}}</td>
<td>
<ul>
<li>Das Ergebnis von <code>ownKeys</code> ist eine Liste.</li>
<li>Der Typ jedes Elements in der Ergebnisliste ist entweder {{jsxref("String")}} oder {{jsxref("Symbol")}}.</li>
<li>Die Ergebnisliste muss alle Schlüssel von nicht konfigurierbaren Eigenschaften des Zielobjektes enthalten.</li>
<li>Wenn das Zielobjekt nicht erweiterbar ist, muss die Ergebnisliste alle Schlüssel des Zielobjektes enthalten und keine anderen Werte.</li>
</ul>
</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/apply", "handler.apply()")}}</td>
<td><code>proxy(..args)</code><br>
{{jsxref("Function.prototype.apply()")}} and {{jsxref("Function.prototype.call()")}}<br>
{{jsxref("Reflect.apply()")}}</td>
<td>Es gibt keine Invarianten für die <code>handler.apply</code> Methode.</td>
</tr>
<tr>
<td>{{jsxref("Global_Objects/Proxy/handler/construct", "handler.construct()")}}</td>
<td><code>new proxy(...args)</code><br>
{{jsxref("Reflect.construct()")}}</td>
<td>Das Ergebnis muss ein <code>Object</code> sein.</td>
</tr>
</tbody>
</table>
<h2 id="Widerrufbarer_Proxy">Widerrufbarer <code>Proxy</code></h2>
<p>Die {{jsxref("Proxy.revocable()")}} Methode wird benutzt, um ein widerrufbares <code>Proxy</code> Objekt zu erstellen. Das bedeutet, dass der Proxy mit der Funktion <code>revoke</code> widerrufen werden kann und der Proxy ausgeschaltet wird. Danach wird jede Operation auf dem Proxy zu einem {{jsxref("TypeError")}} führen.</p>
<pre class="brush: js">var revocable = Proxy.revocable({}, {
get: function(target, name) {
return '[[' + name + ']]';
}
});
var proxy = revocable.proxy;
console.log(proxy.foo); // "[[foo]]"
revocable.revoke();
console.log(proxy.foo); // TypeError is thrown
proxy.foo = 1; // TypeError again
delete proxy.foo; // still TypeError
typeof proxy; // "object", typeof doesn't trigger any trap</pre>
<h2 id="Reflection">Reflection</h2>
<p>{{jsxref("Reflect")}} ist ein Standardobjekt welches Methoden unterstützt, welche das Abfragen von JavaScript Operationen erlauben. Die Methoden sind die gleichen wie die eines {{jsxref("Global_Objects/Proxy/handler","Proxy Handlers","","true")}}. <code>Reflect</code> ist kein Funktionsobjekt.</p>
<p><code>Reflect</code> hilft beim Weiterleiten von Standardoperationen des Handlers zu dem Zielobjekt.</p>
<p>Mit bekommt man {{jsxref("Reflect.has()")}} zum Beispiel den <a href="/de/docs/Web/JavaScript/Reference/Operators/in"><code>in</code> Operator</a> als Funktion:</p>
<pre class="brush: js">Reflect.has(Object, 'assign'); // true
</pre>
<h3 id="Eine_bessere_apply_Funktion">Eine bessere <code>apply</code> Funktion</h3>
<p>In ES5 wird typischerweise die {{jsxref("Function.prototype.apply()")}} Methode genutzt, um eine Funktion mit einem gegebenen <code>this</code> Wert und <code>arguments</code> als Array (oder ein <a href="/de/docs/Web/JavaScript/Guide/Indexed_collections#Mit_Array-ähnlichen_Objekten_arbeiten">Array-ähnliches Objekt</a>) benutzt.</p>
<pre class="brush: js">Function.prototype.apply.call(Math.floor, undefined, [1.75]);</pre>
<p>Mit {{jsxref("Reflect.apply")}} wird dieses weniger Langatmig und leichter verständlich:</p>
<pre class="brush: 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"
</pre>
<h3 id="Prüfen_ob_Eigenschaftsdefinitionen_erfolgreich_waren">Prüfen ob Eigenschaftsdefinitionen erfolgreich waren</h3>
<p>Mit {{jsxref("Object.defineProperty")}}, welche ein Objekt zurück gibt, wenn es erfolgreich war, oder andernfalls ein {{jsxref("TypeError")}} erzeugt, muss man ein {{jsxref("Statements/try...catch","try...catch")}} Block benutzen, um einen Fehler bei der Definition einer Eigenschaft abzufangen. Weil {{jsxref("Reflect.defineProperty")}} einen Boolean als Status zurück gibt, kann man einfach einen {{jsxref("Statements/if...else","if...else")}} Block benutzen:</p>
<pre class="brush: js">if (Reflect.defineProperty(target, property, attributes)) {
// success
} else {
// failure
}</pre>
<p>{{Previous("Web/JavaScript/Guide/Iterators_and_Generators")}}</p>
|