aboutsummaryrefslogtreecommitdiff
path: root/files/pt-br/web/javascript/guide/meta_programming/index.html
blob: e1264f7bcc3d1def5eadc1716c484925bf78df15 (plain)
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
---
title: Meta programação
slug: Web/JavaScript/Guide/Meta_programming
translation_of: Web/JavaScript/Guide/Meta_programming
---
<div>{{jsSidebar("JavaScript Guide")}} {{Previous("Web/JavaScript/Guide/Iterators_and_Generators")}}</div>

<p>Começando com ECMAScript 6, o JavaScript ganha suporte para os objetos {{jsxref("Proxy")}} e {{jsxref("Reflect")}}, permitindo você interceptar e definir o comportamento personalizado para operações fundamentais da linguagem (por exemplo, pesquisa de propriedade, atribuição, enumeração, invocação de função, etc). Com a ajuda destes dois objetos você será capaz de programar a nível <a href="https://pt.wikipedia.org/wiki/Metaprograma%C3%A7%C3%A3o">meta</a> em JavaScript.</p>

<h2 id="Proxies">Proxies</h2>

<p>Introduzido em ECMAScript 6, objetos {{jsxref("Proxy")}} permitem que você intercepte determinadas operações e implementar comportamentos personalizados. Por exemplo, receber uma propriedade em um objeto:</p>

<pre class="brush: js notranslate">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>O objeto Proxy define um <em>target</em> (um objeto vazio aqui) e um objeto <em>handler</em> em que um <code>get</code> <em>trap</em> é implementado. Aqui, um objeto que está em proxy não retornará indefinido quando receber propriedades indefinidas, mas, ao contrário, retornar o número 42.</p>

<p>Exemplos adicionais estão disponíveis na página de referência de {{jsxref("Proxy")}} .</p>

<h3 id="Terminologia">Terminologia</h3>

<p>Os seguintes termos são usados ​​quando se fala sobre a funcionalidade de proxies.</p>

<dl>
 <dt>{{jsxref("Global_Objects/Proxy/handler","handler","","true")}}</dt>
 <dd>Espaço reservado de objeto que contenha traps.</dd>
 <dt>traps</dt>
 <dd>Os métodos que fornecem acesso de propriedade. Isto é análogo ao conceito de traps em sistemas operacionais.</dd>
 <dt>target</dt>
 <dd>Objeto que o proxy está virtualizando. Ele é frequentemente usado como backend de armazenamento para o proxy. Invariantes (semânticas que permanecem inalteradas) relativas a objetos que não podem ser extendidos ou propriedades que não podem ser configuradas são comparadas com o target. </dd>
 <dt>invariantes</dt>
 <dd>Semânticas que permanecem inalteradas na execução de operações personalizadas são chamados de <em>invariantes</em>. Se você violar as invariantes de um manipulador, um {{jsxref("TypeError")}} será lançado.</dd>
</dl>

<h2 id="Handlers_e_traps">Handlers e traps</h2>

<p>A tabela a seguir resume as traps disponíveis aos objetos do tipo Proxy. Veja as <a href="/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler">páginas de referência</a> para explicações detalhadas e exemplos.</p>

<table class="standard-table">
 <thead>
  <tr>
   <th>Handler / trap</th>
   <th>Interceptions</th>
   <th>Invariants</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 style="padding-left: 20px; margin: 5px;">
     <li>O método <code>getPrototypeOf</code> deve retornar um object ou <code>null</code>.</li>
     <li><font face="Open Sans, arial, sans-serif">Se </font><code>target</code> não puder ser extendido, o método <code>Object.getPrototypeOf(proxy)</code> deve retornar o mesmo valor que <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>
    <p>Se <code>target </code>não puder ser extendido, o parâmetro <code>prototype </code>dever ter o mesmo valor que  <code>Object.getPrototypeOf(target)</code>.</p>
   </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> deve retornar o mesmo valor que <code>Object.isExtensible(target)</code>.</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> retorna <code>true</code> somente se <code>Object.isExtensible(proxy)</code> retornar <code>false</code>.</td>
  </tr>
  <tr>
   <td>{{jsxref("Global_Objects/Proxy/handler/getOwnPropertyDescriptor", "handler.getOwnPropertyDescriptor()")}}</td>
   <td>{{jsxref("Object.getOwnPropertyDescriptor()")}}<br>
    {{jsxref("Reflect.getOwnPropertyDescriptor()")}}</td>
   <td>
    <ul style="padding-left: 20px; margin: 5px;">
     <li><code>getOwnPropertyDescriptor</code> deve retornar um object ou <code>undefined</code>.</li>
     <li>Uma propriedade não pode ser descrita como não existente se ela existir como uma propriedade própria e não configurável do objeto alvo.</li>
     <li>Uma propriedade não pode ser relatada como inexistente, se existir como uma propriedade própria do objeto de destino e o objeto de destino não for extensível.</li>
     <li>Uma propriedade não pode ser relatada como existente, se não existir como uma propriedade própria do objeto de destino e o objeto de destino não for extensível.</li>
     <li>Uma propriedade não pode ser relatada como não configurável, se não existir como uma propriedade própria do objeto de destino ou se existir como uma propriedade própria configurável do objeto de destino.</li>
     <li>O resultado de <code>Object.getOwnPropertyDescriptor(target)</code> pode ser aplicado ao objeto de destino usando <code>Object.defineProperty</code> e não emitirá uma exceção.</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 style="padding-left: 20px; margin: 5px;">
     <li>Uma propriedade não pode ser adicionada se o objeto de destino não for extensível.</li>
     <li>Uma propriedade não pode ser adicionada como ou modificada para não ser configurável, se não existir como uma propriedade própria não configurável do objeto de destino.</li>
     <li>Uma propriedade pode não ser não configurável, se existir uma propriedade configurável correspondente do objeto de destino.</li>
     <li>Se uma propriedade tiver uma propriedade de objeto de destino correspondente <code>Object.defineProperty(target, prop, descriptor)</code> não lançará uma exceção.</li>
     <li>No modo estrito, um valor de retorno <code>false</code> do manipulador <code>defineProperty</code> manipulador lançará um {{jsxref("TypeError")}} exceção.</li>
    </ul>
   </td>
  </tr>
  <tr>
   <td>{{jsxref("Global_Objects/Proxy/handler/has", "handler.has()")}}</td>
   <td>Property query: <code>foo in proxy</code><br>
    Inherited property query: <code>foo in Object.create(proxy)</code><br>
    {{jsxref("Reflect.has()")}}</td>
   <td>
    <ul style="padding-left: 20px; margin: 5px;">
     <li>Uma propriedade não pode ser relatada como inexistente, se existir como uma propriedade própria não configurável do objeto de destino.</li>
     <li>Uma propriedade não pode ser relatada como inexistente, se existir como uma propriedade própria do objeto de destino e o objeto de destino não for extensível.</li>
    </ul>
   </td>
  </tr>
  <tr>
   <td>{{jsxref("Global_Objects/Proxy/handler/get", "handler.get()")}}</td>
   <td>Property access: <code>proxy[foo]</code>and <code>proxy.bar</code><br>
    Inherited property access: <code>Object.create(proxy)[foo]</code><br>
    {{jsxref("Reflect.get()")}}</td>
   <td>
    <ul style="padding-left: 20px; margin: 5px;">
     <li>O valor relatado para uma propriedade deve ser igual ao valor da propriedade do objeto de destino correspondente se a propriedade do objeto de destino for uma propriedade de dados não gravável e não configurável.</li>
     <li>O valor relatado para uma propriedade deve ser indefinido se a propriedade do objeto de destino correspondente for uma propriedade acessora não configurável que tenha sido indefinida como seu atributo [[Get]].</li>
    </ul>
   </td>
  </tr>
  <tr>
   <td>{{jsxref("Global_Objects/Proxy/handler/set", "handler.set()")}}</td>
   <td>Property assignment: <code>proxy[foo] = bar</code> and <code>proxy.foo = bar</code><br>
    Inherited property assignment: <code>Object.create(proxy)[foo] = bar</code><br>
    {{jsxref("Reflect.set()")}}</td>
   <td>
    <ul style="padding-left: 20px; margin: 5px;">
     <li>Não é possível alterar o valor de uma propriedade para ser diferente do valor da propriedade do objeto de destino correspondente se a propriedade do objeto de destino correspondente for uma propriedade de dados não gravável e não configurável.</li>
     <li>Não é possível definir o valor de uma propriedade se a propriedade do objeto de destino correspondente for uma propriedade acessadora não configurável que tenha <code>undefined</code> como seu atributo [[Set]].</li>
     <li>No modo estrito, um valor de retorno <code>false</code> do manipulador <code>set</code> lançará uma exceção {{jsxref ("TypeError")}}}.</li>
    </ul>
   </td>
  </tr>
  <tr>
   <td>{{jsxref("Global_Objects/Proxy/handler/deleteProperty", "handler.deleteProperty()")}}</td>
   <td>Property deletion: <code>delete proxy[foo]</code> and <code>delete proxy.foo</code><br>
    {{jsxref("Reflect.deleteProperty()")}}</td>
   <td>Uma propriedade não pode ser excluída, se existir como uma propriedade própria não configurável do objeto de destino.</td>
  </tr>
  <tr>
   <td>{{jsxref("Global_Objects/Proxy/handler/enumerate", "handler.enumerate()")}}</td>
   <td>Property enumeration / for...in: <code>for (var name in proxy) {...}</code><br>
    {{jsxref("Reflect.enumerate()")}}</td>
   <td>
    <p><span>O método </span><code>enumerate</code><span> deve retornar um objeto.</span></p>
   </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 style="padding-left: 20px; margin: 5px;">
     <li>O resultado de <code>ownKeys</code> é uma lista.</li>
     <li>O tipo de cada elemento da lista de resultados é {{jsxref ("String")}} ou {{jsxref ("Symbol")}}.</li>
     <li>A lista de resultados deve conter as chaves de todas as propriedades próprias não configuráveis do objeto de destino.</li>
     <li>Se o objeto de destino não for extensível, a Lista de resultados deverá conter todas as chaves das próprias propriedades do objeto de destino e nenhum outro valor.</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>
    <p>Não há invariantes para o método <code>handler.apply</code></p>
   </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>O resultado deve ser um <code>Object</code>.</td>
  </tr>
 </tbody>
</table>

<h2 id="Proxy_Revogável">Proxy Revogável</h2>

<p>O método {{jsxref("Proxy.revocable()")}} é utilizado para criar um objeto Proxy revogável. Isso significa que o proxy pode ser revogado através da função <code>revoke</code>, desligando-o. Depois disso, qualquer operação com o proxy lançará um {{jsxref("TypeError")}}.</p>

<pre class="brush: js notranslate">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 é lançado
proxy.foo = 1           // TypeError novamente
delete proxy.foo;       // ainda um TypeError
typeof proxy            // "object", typeof não desencadeia nenhuma trap</pre>

<h2 id="Reflexão">Reflexão</h2>

<p>{{jsxref("Reflect")}} é um objeto embutido que contém métodos que permitem a criação  de operações interceptáveis em JavaScript. Os métodos são iguais àqueles de {{jsxref("Global_Objects/Proxy/handler","proxy handlers","","true")}}. <code>Reflect </code>não é um objeto do tipo function.</p>

<p><code>Reflect </code>auxilia no encaminhamento de operações padrão do handler para o target.</p>

<p>{{jsxref("Reflect.has()")}}, por exemplo, tem o mesmo efeito prático que o operador in, com a facilidade de ser utilizado como uma função:</p>

<pre class="brush: js notranslate">Reflect.has(Object, "assign"); // true
</pre>

<h3 id="Uma_função_apply_melhorada">Uma função <code>apply </code>melhorada</h3>

<p>Em ES5, você normalmente utiliza o método {{jsxref("Function.prototype.apply()")}} para invocar uma função com um dado valor para <code>this</code> e <code>arguments</code> fornecido como um array (ou um <a href="/pt-BR/docs/Web/JavaScript/Guide/Indexed_collections#Working_with_array-like_objects">objeto parecido com um array</a>).</p>

<pre class="brush: js notranslate">Function.prototype.apply.call(Math.floor, undefined, [1.75]);</pre>

<p>Com {{jsxref("Reflect.apply")}} essa operação se torna menos verbosa e mais fácil de compreender:</p>

<pre class="brush: js notranslate">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="Verificando_se_a_definição_da_propriedade_obteve_sucesso">Verificando se a definição da propriedade obteve sucesso</h3>

<p>Com {{jsxref("Object.defineProperty")}}, a qual returna um <code>object</code> em caso de sucesso ou lança um  {{jsxref("TypeError")}} em caso contrário, você utilizaria um bloco  {{jsxref("Statements/try...catch","try...catch")}} para capturar qualquer erro que tenha ocorrido ao definir uma propriedade.  Devido ao fato de {{jsxref("Reflect.defineProperty")}} retornar um status do tipo <code>Boolean</code>,  você pode simplesmente utilizar aqui um bloco {{jsxref("Statements/if...else","if...else")}}:</p>

<pre class="brush: js notranslate">if (Reflect.defineProperty(target, property, attributes)) {
  // success
} else {
  // failure
}</pre>

<p>{{Previous("Web/JavaScript/Guide/Iterators_and_Generators")}}</p>