Skip to content

Archives

  • Janeiro 2022
  • Dezembro 2021
  • Novembro 2021
  • Outubro 2021
  • Setembro 2021

Categories

  • Sem categorias
Trend RepositoryArticles and guides
Articles

JavaScript Decoradores: O que são e quando usá-los

On Janeiro 26, 2022 by admin

Com a introdução do ES2015+, e como a transposição se tornou comum, muitos de vocês terão se deparado com novas funcionalidades da linguagem, seja em código real ou em tutoriais. Uma destas funcionalidades que muitas vezes tem pessoas a coçar a cabeça quando se deparam pela primeira vez são os decoradores JavaScript.

Decoradores tornaram-se populares graças ao seu uso no Angular 2+. Em Angular, os decoradores estão disponíveis graças ao TypeScript, mas em JavaScript eles são atualmente uma proposta estágio 2, o que significa que eles devem ser parte de uma atualização futura da linguagem. Vamos ver o que são os decoradores, e como podem ser usados para tornar o seu código mais limpo e mais facilmente compreensível.

  • O que é um Decorador?
  • Como usar decoradores JavaScript
  • Porquê usar decoradores?
  • Diferentes Tipos de Decoradores
  • Class member decorators
  • Class decorators
  • Exemplos do Mundo Real
  • Core decoradores
  • Reagir
  • MobX
  • Sumário

O que é um Decorador?

Na sua forma mais simples, um decorador é simplesmente uma forma de embrulhar uma peça de código com outra – literalmente “decorá-la”. Este é um conceito que já deve ter ouvido falar anteriormente como composição funcional, ou funções de ordem superior.

Isto já é possível no JavaScript padrão para muitos casos de uso, simplesmente chamando uma função para envolver outra:

function doSomething(name) { console.log('Hello, ' + name);}function loggingDecorator(wrapped) { return function() { console.log('Starting'); const result = wrapped.apply(this, arguments); console.log('Finished'); return result; }}const wrapped = loggingDecorator(doSomething);

Este exemplo produz uma nova função – na variável wrapped – que pode ser chamada exactamente da mesma forma que a função doSomething, e fará exactamente a mesma coisa. A diferença é que fará algum registo antes e depois da função embrulhada ser chamada:

doSomething('Graham');// Hello, Grahamwrapped('Graham');// Starting// Hello, Graham// Finished

Como usar decoradores JavaScript

Decoradores usam uma sintaxe especial em JavaScript, onde são prefixados com um símbolo @ e colocados imediatamente antes do código ser decorado.

Nota: no momento da escrita, os decoradores estão actualmente em “Stage 2 Draft”, o que significa que estão na sua maioria acabados mas ainda sujeitos a alterações.

É possível utilizar tantos decoradores no mesmo código quantos desejar, e eles serão aplicados na ordem em que os declarar.

Por exemplo:

@log()@immutable()class Example { @time('demo') doSomething() { // }}

Esta define uma classe e aplica três decoradores – dois à própria classe, e um a uma propriedade da classe:

  • @log poderia registrar todo o acesso à classe
  • @immutable poderia tornar a classe imutável – talvez ela chame Object.freeze em novas instâncias
  • @time irá registrar o tempo que um método leva para executar e registrar isso com uma tag única.

No momento, usar decoradores requer suporte a transpiler, já que nenhum navegador ou lançamento de Node atual tem suporte a eles ainda. Se você estiver usando Babel, isto é habilitado simplesmente usando o plugin transform-decoradores-legacy.

Note: o uso da palavra “legado” neste plugin é porque suporta a forma Babel 5 de lidar com decoradores, que pode muito bem ser diferente da forma final quando eles são padronizados.

Porquê usar decoradores?

Se já é possível a composição funcional em JavaScript, é significativamente mais difícil – ou mesmo impossível – aplicar as mesmas técnicas a outras peças de código (por exemplo, classes e propriedades das classes).

A proposta do decorador adiciona suporte para decoradores de classes e propriedades que podem ser usados para resolver estes problemas, e futuras versões JavaScript provavelmente adicionarão suporte para decoradores para outras áreas problemáticas do código.

Decoradores também permitem uma sintaxe mais limpa para aplicar estes wrappers à volta do seu código, resultando em algo que diminui menos a intenção real do que está a escrever.

Diferentes Tipos de Decoradores

No presente, os únicos tipos de decoradores que são suportados estão em classes e membros de classes. Isto inclui propriedades, métodos, getters e setters.

Decoradores nada mais são do que funções que retornam outra função, e que são chamadas com os detalhes apropriados do item sendo decorado. Estas funções de decoradores são avaliadas uma vez quando o programa é executado pela primeira vez, e o código decorado é substituído pelo valor de retorno.

Class member decorators

Property decorators are applied to a single member in a class – whether they are properties, methods, getters, or setters. Esta função decorador é chamada com três parâmetros:

  • target: a classe em que o membro está.
  • name: o nome do membro na classe.
  • descriptor: o descritor de membro. Este é essencialmente o objeto que teria sido passado para Object.defineProperty.

: o exemplo clássico usado aqui é @readonly. Isto é implementado tão simplesmente como:

function readonly(target, name, descriptor) { descriptor.writable = false; return descriptor;}

Atualizando literalmente o descritor de propriedades para definir o flag “escrevível” como falso.

Isto é então usado em uma propriedade de classe como segue:

class Example { a() {} @readonly b() {}}const e = new Example();e.a = 1;e.b = 2;// TypeError: Cannot assign to read only property 'b' of object '#<Example>'

Mas podemos fazer melhor do que isto. Na verdade, podemos substituir a função decorada por um comportamento diferente. Por exemplo, vamos registrar todas as entradas e saídas:

function log(target, name, descriptor) { const original = descriptor.value; if (typeof original === 'function') { descriptor.value = function(...args) { console.log(`Arguments: ${args}`); try { const result = original.apply(this, args); console.log(`Result: ${result}`); return result; } catch (e) { console.log(`Error: ${e}`); throw e; } } } return descriptor;}

Isso substitui todo o método por um novo que registra os argumentos, chama o método original e então registra a saída.

Nota que usamos o operador de spread aqui para construir automaticamente um array a partir de todos os argumentos fornecidos, que é a alternativa mais moderna ao antigo arguments valor.

Podemos ver isso em uso da seguinte forma:

class Example { @log sum(a, b) { return a + b; }}const e = new Example();e.sum(1, 2);// Arguments: 1,2// Result: 3

Você vai notar que tivemos que usar uma sintaxe ligeiramente engraçada para executar o método decorado. Isto poderia cobrir um artigo inteiro, mas em resumo, a função apply permite chamar a função, especificando o valor this e os argumentos para a chamar com.

Acima de um entalhe, podemos arranjar para o nosso decorador levar alguns argumentos. Por exemplo, vamos reescrever o nosso log decorador como se segue:

function log(name) { return function decorator(t, n, descriptor) { const original = descriptor.value; if (typeof original === 'function') { descriptor.value = function(...args) { console.log(`Arguments for ${name}: ${args}`); try { const result = original.apply(this, args); console.log(`Result from ${name}: ${result}`); return result; } catch (e) { console.log(`Error from ${name}: ${e}`); throw e; } } } return descriptor; };}

Isto está a ficar mais complexo agora, mas quando o decompomos temos isto:

  • Uma função, log, que leva um único parâmetro: name.
  • Esta função retorna então uma função que é ela própria um decorador.

Esta é idêntica à anterior log decorador, excepto que faz uso do parâmetro name da função externa.

Esta é então usada da seguinte forma:

class Example { @log('some tag') sum(a, b) { return a + b; }}const e = new Example();e.sum(1, 2);// Arguments for some tag: 1,2// Result from some tag: 3

Apartir de uma linha de log, podemos ver que isto nos permite distinguir entre diferentes linhas de log usando uma tag que nós próprios fornecemos.

>

Isto funciona porque a chamada de função log('some tag') é avaliada imediatamente pelo tempo de execução do JavaScript, e depois a resposta a partir daí é usada como o decorador para o método sum.

Class decorators

Class decorators are applied to the whole class definition all in one go. A função decorator é chamada com um único parâmetro que é a função construtor sendo decorada.

Note que isto é aplicado à função construtor e não a cada instância da classe que é criada. Isto significa que se você quiser manipular as instâncias você mesmo precisa fazê-lo retornando uma versão embrulhada do construtor.

Em geral, estas são menos úteis que os decoradores membros da classe, porque tudo que você pode fazer aqui você pode fazer com uma simples chamada de função exatamente da mesma forma. Qualquer coisa que você faça com elas precisa acabar retornando uma nova função de construtor para substituir o construtor da classe.

Voltando ao nosso exemplo de registro, vamos escrever uma que registre os parâmetros do construtor:

function log(Class) { return (...args) => { console.log(args); return new Class(...args); };}

Aqui estamos aceitando uma classe como nosso argumento, e retornando uma nova função que atuará como o construtor. Isso simplesmente registra os argumentos e retorna uma nova instância da classe construída com esses argumentos.

Por exemplo:

@logclass Example { constructor(name, age) { }}const e = new Example('Graham', 34);// console.log(e);// Example {}

Vemos que a construção da nossa classe Exemplo irá logar os argumentos fornecidos e que o valor construído é de fato uma instância de Example. Exatamente o que queríamos.

Passar parâmetros em decoradores de classe funciona exatamente como para os membros da classe:

function log(name) { return function decorator(Class) { return (...args) => { console.log(`Arguments for ${name}: args`); return new Class(...args); }; }}@log('Demo')class Example { constructor(name, age) {}}const e = new Example('Graham', 34);// Arguments for Demo: argsconsole.log(e);// Example {}

Exemplos do Mundo Real

Core decoradores

Existe uma biblioteca fantástica chamada Core Decorators que fornece alguns decoradores comuns muito úteis que estão prontos para usar no momento. Estes geralmente permitem funcionalidades comuns muito úteis (por exemplo, tempo de chamadas de métodos, avisos de depreciação, assegurando que um valor é apenas de leitura) mas utilizando a sintaxe muito mais limpa do decorador.

Reagir

A biblioteca Reagir faz muito bom uso do conceito de Componentes de Ordem Superior. Estes são simplesmente componentes React que são escritos como uma função, e que envolvem outro componente.

Comprar o nosso curso Premium: Reage The ES6 Way

Estes são um candidato ideal para usar como decorador, porque há muito pouco que você precisa mudar para fazer isso. Por exemplo, a biblioteca React redux tem uma função, connect, que é usada para conectar um componente React a uma loja Redux.

Em geral, isto seria usado da seguinte forma:

class MyReactComponent extends React.Component {}export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);

No entanto, devido à forma como a sintaxe do decorador funciona, isto pode ser substituído pelo seguinte código para obter exactamente a mesma funcionalidade:

@connect(mapStateToProps, mapDispatchToProps)export default class MyReactComponent extends React.Component {}

MobX

A biblioteca MobX faz um uso extensivo dos decoradores, permitindo-lhe marcar facilmente campos como Observáveis ou Computed, e marcar classes como Observadores.

Sumário

Os decoradores de membros de classe fornecem uma maneira muito boa de embrulhar o código dentro de uma classe de forma muito semelhante à forma como você já pode fazer isso para funções independentes. Isto fornece uma boa maneira de escrever algum código simples de ajuda que pode ser aplicado em muitos lugares de uma maneira muito limpa e fácil de entender.

O único limite para usar tal facilidade é a sua imaginação!

Deixe uma resposta Cancelar resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *

Arquivo

  • Janeiro 2022
  • Dezembro 2021
  • Novembro 2021
  • Outubro 2021
  • Setembro 2021

Meta

  • Iniciar sessão
  • Feed de entradas
  • Feed de comentários
  • WordPress.org
  • DeutschDeutsch
  • NederlandsNederlands
  • SvenskaSvenska
  • DanskDansk
  • EspañolEspañol
  • FrançaisFrançais
  • PortuguêsPortuguês
  • ItalianoItaliano
  • RomânăRomână
  • PolskiPolski
  • ČeštinaČeština
  • MagyarMagyar
  • SuomiSuomi
  • 日本語日本語

Copyright Trend Repository 2022 | Theme by ThemeinProgress | Proudly powered by WordPress