Meta programação
================

* « Previous

Começando com ECMAScript 6, o JavaScript ganha suporte para os objetos `Proxy` e `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 meta em JavaScript.

Proxies
-------

Introduzido em ECMAScript 6, objetos `Proxy` permitem que você intercepte determinadas operações e implementar comportamentos personalizados. Por exemplo, receber uma propriedade em um objeto:


```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
```


O objeto Proxy define um `target` (um objeto vazio aqui) e um objeto `handler` em que um `get` *trap* é 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.

Exemplos adicionais estão disponíveis na página de referência de `Proxy` .

### Terminologia

Os seguintes termos são usados quando se fala sobre a funcionalidade de proxies.

handler (en-US)

Espaço reservado de objeto que contenha traps.

traps

Os métodos que fornecem acesso de propriedade. Isto é análogo ao conceito de traps em sistemas operacionais.

target

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.

invariantes

Semânticas que permanecem inalteradas na execução de operações personalizadas são chamados de *invariantes*. Se você violar as invariantes de um manipulador, um `TypeError` será lançado.

Handlers e traps
----------------

A tabela a seguir resume as traps disponíveis aos objetos do tipo Proxy. Veja as páginas de referência (en-US) para explicações detalhadas e exemplos.

| Handler / trap | Interceptions | Invariants |
| --- | --- | --- |
| `handler.getPrototypeOf()` (en-US) | `Object.getPrototypeOf()``Reflect.getPrototypeOf()` (en-US)`__proto__``Object.prototype.isPrototypeOf()``instanceof` | * O método `getPrototypeOf` deve retornar um object ou
 `null`.
* Se `target` não puder ser extendido, o método
 `Object.getPrototypeOf(proxy)` deve retornar o mesmo
 valor que `Object.getPrototypeOf(target)`.
 |
| `handler.setPrototypeOf()` (en-US) | `Object.setPrototypeOf()``Reflect.setPrototypeOf()` (en-US) | 
 Se `target` não puder ser extendido, o parâmetro
 `prototype` dever ter o mesmo valor que
 `Object.getPrototypeOf(target)`.
  |
| `handler.isExtensible()` (en-US) | `Object.isExtensible()``Reflect.isExtensible()` (en-US) | `Object.isExtensible(proxy)` deve retornar o mesmo valor que
 `Object.isExtensible(target)`.
  |
| `handler.preventExtensions()` (en-US) | `Object.preventExtensions()``Reflect.preventExtensions()` (en-US) | `Object.preventExtensions(proxy)` retorna
 `true` somente se
 `Object.isExtensible(proxy)` retornar `false`.
  |
| `handler.getOwnPropertyDescriptor()` (en-US) | `Object.getOwnPropertyDescriptor()``Reflect.getOwnPropertyDescriptor()` (en-US) | * `getOwnPropertyDescriptor` deve retornar um object ou
 `undefined`.
* 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.
* 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.
* 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.
* 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.
* O resultado de
 `Object.getOwnPropertyDescriptor(target)` pode ser
 aplicado ao objeto de destino usando
 `Object.defineProperty` e não emitirá uma exceção.
 |
| `handler.defineProperty()` (en-US) | `Object.defineProperty()``Reflect.defineProperty()` | * Uma propriedade não pode ser adicionada se o objeto de destino não
 for extensível.
* 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.
* Uma propriedade pode não ser não configurável, se existir uma
 propriedade configurável correspondente do objeto de destino.
* Se uma propriedade tiver uma propriedade de objeto de destino
 correspondente
 `Object.defineProperty(target, prop, descriptor)` não
 lançará uma exceção.
* No modo estrito, um valor de retorno `false` do
 manipulador `defineProperty` manipulador lançará um
 `TypeError` exceção.
 |
| `handler.has()` (en-US) | 
 Property query: `foo in proxy`Inherited property query:
 `foo in Object.create(proxy)``Reflect.has()` (en-US) | * Uma propriedade não pode ser relatada como inexistente, se existir
 como uma propriedade própria não configurável do objeto de destino.
* 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.
 |
| `handler.get()` (en-US) | 1
 Property access: `proxy[foo]`and `proxy.bar`Inherited property access: `Object.create(proxy)[foo]``Reflect.get()` (en-US) | * 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.
* 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]].
 |
| `handler.set()` (en-US) | 
 Property assignment: `proxy[foo] = bar` and
 `proxy.foo = bar`Inherited property assignment:
 `Object.create(proxy)[foo] = bar``Reflect.set()` | * 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.
* 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 `undefined` como seu atributo
 [[Set]].
* No modo estrito, um valor de retorno `false` do
 manipulador `set` lançará uma exceção
 `TypeError`.
 |
| `handler.deleteProperty()` (en-US) | 
 Property deletion: `delete proxy[foo]` and
 `delete proxy.foo``Reflect.deleteProperty()` (en-US) | 
 Uma propriedade não pode ser excluída, se existir como uma propriedade
 própria não configurável do objeto de destino.
  |
| `handler.enumerate()` | 
 Property enumeration / for...in:
 `for (var name in proxy) {...}``Reflect.enumerate()` | O método `enumerate` deve retornar um objeto. |
| `handler.ownKeys()` (en-US) | `Object.getOwnPropertyNames()``Object.getOwnPropertySymbols()``Object.keys()``Reflect.ownKeys()` (en-US) | * O resultado de `ownKeys` é uma lista.
* O tipo de cada elemento da lista de resultados é
 `String` ou `Symbol`.
* A lista de resultados deve conter as chaves de todas as propriedades
 próprias não configuráveis do objeto de destino.
* 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.
 |
| `handler.apply()` (en-US) | `proxy(..args)``Function.prototype.apply()` and
 `Function.prototype.call()``Reflect.apply()` | Não há invariantes para o método `handler.apply` |
| `handler.construct()` (en-US) | `new proxy(...args)``Reflect.construct()` | O resultado deve ser um `Object`. |

Proxy Revogável
---------------

O método `Proxy.revocable()` (en-US) é utilizado para criar um objeto Proxy revogável. Isso significa que o proxy pode ser revogado através da função `revoke`, desligando-o. Depois disso, qualquer operação com o proxy lançará um `TypeError`.


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

Reflexão
--------

`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 proxy handlers (en-US). `Reflect` não é um objeto do tipo function.

`Reflect` auxilia no encaminhamento de operações padrão do handler para o target.

`Reflect.has()` (en-US), por exemplo, tem o mesmo efeito prático que o operador in, com a facilidade de ser utilizado como uma função:


```js
Reflect.has(Object, "assign"); // true
```

### Uma função `apply` melhorada

Em ES5, você normalmente utiliza o método `Function.prototype.apply()` para invocar uma função com um dado valor para `this` e `arguments` fornecido como um array (ou um objeto parecido com um array).


```js
Function.prototype.apply.call(Math.floor, undefined, [1.75]);
```


Com `Reflect.apply` essa operação se torna menos verbosa e mais fácil de compreender:


```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"
```

### Verificando se a definição da propriedade obteve sucesso

Com `Object.defineProperty`, a qual retorna um `object` em caso de sucesso ou lança um `TypeError` em caso contrário, você utilizaria um bloco `try...catch` para capturar qualquer erro que tenha ocorrido ao definir uma propriedade. Devido ao fato de `Reflect.defineProperty` retornar um status do tipo `Boolean`, você pode simplesmente utilizar aqui um bloco `if...else`:


```js
if (Reflect.defineProperty(target, property, attributes)) {
  // success
} else {
  // failure
}
```

* « Previous