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.
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.
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. Proxy
fornece 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.
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.
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).
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.salary
200
. 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.
💡 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.