Introdução

De acordo com o dicionário de Cambridge , para decorar algo significa “adicionar algo a um objeto ou lugar, especialmente para torná-lo mais atraente.”

Decorar na programação é simplesmente embrulhar um trecho de código com outro, decorando-o assim. Um decorador (também conhecido como função de decorador) pode referir-se adicionalmente ao padrão de design que envolve uma função com outra função para estender sua funcionalidade.

Esse conceito é possível em JavaScript por causa de funções de primeira classe-JavaScript funções que são tratadas como cidadãos de primeira classe .

O conceito de decoradores não é novo em JavaScript porque as funções de ordem superior são uma forma de decoradores de função.

Vamos elaborar isso na próxima seção.

Decoradores de função

Decoradores de função são funções. Eles tomam uma função como um argumento e retornam uma nova função que aprimora o argumento da função sem modificá-lo.

Funções de ordem superior

Em JavaScript, as funções de ordem superior recebem um primeiro class function como um argumento e/ou retornar outras funções.

Considere o código abaixo:

const logger=(message)=> console.log (message) function loggerDecorator (logger) {return function (mensagem) {logger.call (this, message) console.log (“mensagem registrada em:”, new Date (). toLocaleString ())}} const decorativeLogger=loggerDecorator (logger);

Nós decoramos a função logger usando a função loggerDecorator. A função retornada-agora armazenada na variável decorativeLogger-não modifica a função de logger. Em vez disso, a função retornada o decora com a capacidade de imprimir a hora em que uma mensagem é registrada.

Considere o código abaixo:

logger (“Lawrence conectado: logger”)//retorna Lawrence registrado in: logger associatedLogger (“Lawrence conectado: decoradoLogger”)//retorna://Lawrence conectado: decoradoLogger//mensagem registrada em: 20/06/2021, 9:18:39 PM

Vemos isso quando o função logger é chamada, ele registra a mensagem no console. Mas quando a função decorada é chamada, ela registra a mensagem e a hora atual no console.

Abaixo está outro exemplo sensato de um decorador de função:

//função de multiplicação comum let Multiply=(… args)=> {return args.reduce ((a, b)=> a * b)}//inteiros validados const Validator=(fn)=> {return function (… args) {const validArgs=args.every (arg=> Number.isInteger (arg)); if (! validArgs) {throw new TypeError (‘O argumento não pode ser um número não inteiro’); } return fn (… args); }}//função de multiplicação decorada que multiplica apenas inteiros MultiplyValidArgs=Validator (Multiply); MultiplyValidArgs (6, 8, 2, 10);

Em nosso código acima, temos uma função Multiply comum que nos dá o produto de todos os seus argumentos. No entanto, com nossa função Validator-que é um decorador-estendemos a funcionalidade de nossa função Multiply para validar sua entrada e multiplicar apenas números inteiros.

Decoradores de classe

Em JavaScript, decoradores de função existem, pois a linguagem oferece suporte a funções de ordem superior. O padrão usado em decoradores de função não pode ser usado facilmente em classes JavaScript. Portanto, a proposta do decorador de classe TC39 . Você pode aprender mais sobre o processo TC39 aqui .

A proposta do decorador de classe TC39 visa resolver esse problema:

function log (fn) {return function () {console.log (“Logado em:”+ new Date (). toLocaleString ()); return fn (); }} classe Person {constructor (name, age, job) {this.name=name; this.age=idade; this.job=job; } getBio () {return `$ {this.name} tem $ {this.age} anos $ {this.job}`; }}//cria uma nova pessoa let man=new Person (“Lawrence”, 20,”desenvolvedor”);//decora o método getBio let decorativeGetBio=log (man.getBio); decoradoGetBio ();//TypeError: Não é possível ler a propriedade’name’de undefined em getBio

Tentamos decorar o método getBio usando a técnica do decorador de função, mas não funcionou. Obtemos um TypeError porque quando o método getBio é chamado dentro da função log, a variável this refere-se à função interna ao objeto global.

Podemos contornar isso vinculando a variável this à instância man de a classe Person conforme mostrado abaixo:

//decora o método getBio let decorativeGetBio=log (man.getBio.bind (man)); decoradoGetBio ();//retorna//Registrado em: 22/06/2021, 11:56:57//Lawrence é um desenvolvedor de 20 anos

Embora funcione, requer um pouco de hack e um bom entendimento do JavaScript esta variável. Portanto, há uma necessidade de um método mais limpo e fácil de entender de usar decoradores com classes.

Decoradores de classe-ou estritamente decoradores-são uma proposta para estender classes JavaScript. TC39 é atualmente uma proposta de estágio 2 , o que significa que se espera que sejam desenvolvidos e eventualmente incluídos no idioma.

No entanto, com a introdução do ES2015 +, e como a transpilação se tornou comum, podemos usar esse recurso com a ajuda de ferramentas como Babel .

Os decoradores usam uma sintaxe especial em que são prefixados com um símbolo @ e colocados imediatamente acima do código que está sendo decorado, como visto abaixo:

@log class ExampleClass {doSomething () {//}}

Tipos de decoradores de classe

Atualmente, os tipos de decoradores suportados estão em classes e membros de classes-como métodos, getters e setters.

Vamos aprender mais sobre eles abaixo.

Decoradores de membros de classe

Um decorador de membros de classe é uma função ternária aplicada a membros de uma classe. Ele tem os seguintes parâmetros:

Destino-refere-se à classe que contém a propriedade do membro Nome-refere-se ao nome da propriedade do membro que estamos decorando na classe Descritor-este é o objeto descritor com as seguintes propriedades: valor, gravável, enumerável e configurável

A propriedade valor do objeto descritor refere-se à propriedade membro da classe que estamos decorando. Isso torna possível um padrão onde podemos substituir nossa função decorada.

Vamos aprender sobre isso reescrevendo nosso decorador de log:

log de função (destino, nome, descritor) {if (typeof original===’função’) {descriptor.value=function (… args) {console.log (“Logado em:”+ new Date (). toLocaleString ()); tente {const result=original.apply (this, args); resultado de retorno; } catch (e) {console.log (`Erro: $ {e}`); jogue e; }}} descritor de retorno; } class Person {constructor (name, age, job) {this.name=name; this.age=idade; this.job=job; } @log getBio () {return `$ {this.name} tem $ {this.age} anos $ {this.job}`; }}//cria uma nova pessoa let man=new Person (“Lawrence”, 20,”desenvolvedor”); man.getBio ()

No código acima, refatoramos com sucesso nosso decorador de log-de padrão de decorador de função para decorador de classe de membro.

Simplesmente acessamos a propriedade da classe de membro-neste caso, o Método getBio-com o valor do descritor e substituído por uma nova função.

Isso é mais limpo e pode ser mais facilmente reutilizado do que funções simples de ordem superior.

Decoradores de classe

Esses decoradores são aplicados a toda a classe, permitindo-nos decorar a classe.

A função do decorador da classe é uma função unária que recebe a função construtora que está sendo decorada como um argumento.

Considere o código abaixo:

function log (target) {console.log (“target is:”, target,); return (… args)=> {console.log (args); retornar novo destino (… args); }; } @log class Person {construtor (nome, profissão) {}} const lawrence=new Person (‘Lawrence Eagles’,”Desenvolvedor”); console.log (lawrence);//retorna//target is: [Function: Person]//[‘Lawrence Eagles’,’Developer’]//Person {}

Em nosso pequeno exemplo inventado, registramos o argumento alvo-a função construtora-e os argumentos fornecidos antes de retornar uma instância da classe construída com esses argumentos.

Por que decoradores?

Os decoradores nos permitem escrever um código mais limpo, fornecendo uma maneira eficiente e compreensível de embrulhar um pedaço de código com outro. Ele também fornece uma sintaxe limpa para aplicar esse wrapper.

Essa sintaxe torna nosso código menos perturbador porque separa o código de aprimoramento de recursos da função principal. E nos permite adicionar novos recursos sem aumentar a complexidade do código.

Além disso, os decoradores nos ajudam a estender a mesma funcionalidade a várias funções e classes, permitindo-nos escrever um código mais fácil de depurar e manter.

Embora os decoradores já existam no JavaScript como funções de ordem superior , é difícil ou mesmo impossível implementar esta técnica nas aulas. Portanto, a sintaxe especial que TC39 oferece é para fácil uso com classes.

Conclusão

Embora decoradores sejam uma proposta de estágio 2, eles já são populares no mundo do JavaScript-graças ao Angular e TypeScript .

A partir deste artigo, podemos ver que eles promovem a reutilização do código, mantendo assim nosso código DRY.

Enquanto esperamos que os decoradores estejam oficialmente disponíveis em JavaScript, você pode começar a usá-los usando o Babel. E eu acredito que você aprendeu o suficiente neste artigo para dar uma chance aos decoradores em seu próximo projeto.