Articles

a brief introduction to Metaprogramming in JavaScript

Reflection

Reflection, unliked “code generation”, is a process to change the underlying mechanics of the language. A reflexão pode acontecer em tempo de compilação ou em tempo de execução, mas nós vamos ficar com a reflexão em tempo de execução como estamos falando sobre JavaScript, então a reflexão em tempo de compilação não será possível. No entanto, os conceitos aqui discutidos poderiam ser aplicáveis a uma linguagem compilada também.

Como temos entendido que a reflexão é sobre a mudança da mecânica subjacente da linguagem, ela foi dividida em três categorias principais, viz. introspecção, intercessão e modificação.

introspecção

introspecção é o processo de análise do programa. Se você pode dizer o que o programa faz, você pode modificá-lo de acordo com seus likings. Mesmo que algumas linguagens de programação não suportam a geração de código ou características de modificação de código, mas eles mais provavelmente permitem introspecção.

Um exemplo simples de introspecção seria o uso de typeof ou instanceof operadores em JavaScript. typeof retorna o tipo de dados atual de um valor (ou uma expressão que retorna um valor), enquanto instanceof retorna true ou false se LHS valor é uma instância de RHS de classe. Vamos vê-los em acção.

(introdução/introspecção.js)

No programa acima, nós temos usado typeof e instanceof operadores coerce função para controlar o tipo de dados de entrada value. Esta é a demonstração básica de introspecção. No entanto, uma linguagem especificamente concebida para a metaprogramação pode fornecer algumas ferramentas de introspecção poderosas.

pode usar o operador in para verificar se existe uma propriedade no objecto. TheisNaN global function checks if object is. Existem alguns métodos estáticos construído em torno de Object para verificar valores de Object como Object.isFrozen(value) para verificar se value é congelada ou Object.keys(value) para obter os nomes de propriedades de value objeto.

até ES5, tínhamos estes operadores e estes métodos para trabalhar. Em ES2015 (ES6), JavaScript introduzido Reflect objeto que fornece alguns métodos estáticos (como Object), mas especificamente projetado para introspecção. Uma vez que temos uma lição separada sobre Reflect, estes métodos são discutidos lá.

Intercessão

Intercessão é o processo de intervir nos processos JavaScript e modificar o comportamento padrão do processo. JavaScript provides great tools for intercession one of which is Proxy.

Proxy classe foi introduzido no ES2015 (ES6) para interceptar (intervir) JavaScript básico de operações ao redor de objetos apenas como vimos acima, mas em muito melhor forma. Temos uma lição separada sobre Proxy (coming soon) but in a nutshell, Proxy wraps an interceptable logic around an object.

var targetWithProxy = new Proxy(target, handler);

Aqui target é objeto e handler é o interceptor. O handler é também um objeto javascript simples, mas com alguns campos significativos. For example, handler.get would be a function that returns a custom value when target.prop (here, prop is any property) is accessed.

(introduction/proxy.js)

Proxy is a great way to provide abstractions over your not-so-public data. Por exemplo, no programa acima, temos fornecido abstrações sobre target objeto e personalizado como ele deve se apresentar ao público.

Alguns intercessão foi possível em ES5, bem como o uso de getter e setters no descritores de propriedade, mas isso resultaria em uma mutação de target objeto. Proxyfornece uma maneira muito mais limpa de conseguir a intercessão sem ter que modificar o objeto original (target).

modificação

modificação refere-se à modificação do comportamento do programa através da mutação. No caso de intercessão, apenas interceptamos os processos JavaScript padrão adicionando uma lógica de intercepção entre o alvo e o receptor sem prejudicar o alvo. Neste caso de modificação, estamos mudando o comportamento do próprio alvo para se adequar ao receptor.

anular uma implementação de função seria um bom exemplo de modificação. Por exemplo, se uma função é projetada para se comportar de uma certa forma, mas queremos outra coisa condicionalmente, podemos fazer isso projetando uma função auto-imperante. Vejamos um exemplo.

(introdução/função-modificação.js)

no exemplo acima, nós criamos uma função que se sobrepõe com uma nova implementação da função. Este seria o exemplo mais duro de modificação, mas temos outros casos de uso, talvez mais significativos.

(introdução/readonly-objeto.js)

No exemplo acima, temos usado Object.defineProperty() método para alterar a propriedade padrão descritor de name propriedade, a fim de torná-lo só de leitura. Você também pode usar o método Object.freeze() para bloquear todo o objeto para evitar quaisquer mutações.algumas intercessões podem acontecer através de modificações como você pode a partir do exemplo acima. Ao definir writable:false no descritor de propriedade do objeto, portanto, alterando o objeto (implementação interna), temos inceptado a operação de atribuição de valor.

Se você não está familiarizado com o método valueOf, ele é usado para coagir um objeto a um valor primitivo. Então, se eu tenho um objeto e ele tem valueOf método em si mesmo ou em sua cadeia protótipo, então este método é chamado por JavaScript quando você está tentando executar uma operação aritmética nele. Por padrão, Object tem ovalueOf método que retorna a si mesmo (o objeto).

(introdução/valueof.js)

Como você pode ver no exemplo acima, emp1/10 resultou em um NaN desde que um objeto não pode ser dividido, como números naturais. Mas, mais tarde, nós adicionamos valueOf método Employee classe que retorna salary valor do objeto. Portanto emp2/10 devolvidos 200 desde emp2.salary200. Da mesma forma, emp3/10 devolvidos 300 como nós adicionamos valueOf método diretamente sobre o emp3.

assim, a cada passo no exemplo acima, estamos intervindo como um objeto é apresentado a uma operação padrão de JavaScript e mudando seu comportamento para os nossos likings. Isto não é mais do que a intercessão.

In ES2015 (ES6), JavaScript has introduced a new primitive data type which issymbol. Não é nada como já vimos antes e não pode ser representado na forma literal. Ele só pode ser construído chamando a função Symbol.

var sym1 = Symbol();
var sym2 = Symbol();
var sym3 = Symbol('description'); // description for debugging aidsym1 === sym2 // false
sym1 === sym2 // falsetypeof sym1 // 'symbol'console.log( sym1 ); // 'Symbol()'
console.log( sym3 ); // 'Symbol(description)'

Em poucas palavras, ele produz valores exclusivos que podem ser usados também como regular chaves de objeto usando o obj notação de onde key seria um símbolo.

var key = Symbol();var obj = {
name: 'Ross',
: 200
};console.log( obj.name ); // 'Ross'
console.log( obj ); // 200
Object.keys(obj); // obj = 300;

Uma vez que eles são únicos, não há maneira de criar um símbolo duplicado por acidente. Cada símbolo Novo é único (criado com Symbol()), o que significa que, se quiser usar um mesmo símbolo, deve guardá-lo numa variável e passar essa variável para se referir ao mesmo símbolo.

no exemplovalueOf, você pode detectar o problema se não estivermos sendo cuidadosos ou conscientes. Desde valueOf é um string propriedades (como emp3) , qualquer pessoa pode substituí-la acidentalmente ou alguém que não sabe sobre o valueOf poderia planeja usá-lo para seu próprio uso pensar “qual é o nome?”.

Uma vez que os símbolos também podem ser usados como chaves de objetos, JavaScript forneceu alguns símbolos globais que devem ser usados como chaves de objetos para algumas operações Padrão de JavaScript. Uma vez que estes símbolos são bem conhecidos de um desenvolvedor, eles são chamados de “símbolos bem conhecidos”. Estes símbolos bem conhecidos são expostos ao público como as propriedades estáticas de Symbol função.

um dos símbolos bem conhecidos é Symbol.toPrimitive que deve ser usado como a chave do objeto para obter o seu valor primitivo. Sim, você está pensando direito, é uma substituição de valueOf método e é preferido.

(introdução/símbolo-toPrimitive.js)

💡 The toPrimitive method does more than just returning a number value of the object. Por favor, leia as lições de símbolos para saber mais sobre ele.

JavaScript provides many such well-known symbols to intercept and modify the default JavaScript behavior around objects. Vamos falar sobre isso e símbolos em geral na lição de símbolos.