Em JavaScript, o Module Pattern ou Padrão de Módulo é um padrão de codificação bastante comum. Em geral, é bem compreendido, mas há uma série de usos avançados que podem passar despercebidos. Neste artigo, veja os conceitos básicos sobre o Module Pattern e algumas informações mais avançadas sobre o padrão.

O básico sobre Module Pattern

Para começar, vamos a uma visão global sobre o Padrão de Módulo em JavaScript, já conhecido desde o post de Eric Miraglia a respeito há alguns anos atrás — mesmo que você já tenha lido o artigo sobre JavaScript IIFE como contêiner de códigos, talvez seja interessante rever o básico.

Anonymous Closures (Closures Anônimos)

Anonymous Closures são o construto fundamental em Module Pattern que tornam tudo isso possível e são uma das melhores característica de JavaScript: é possível criar uma função anônima e executá-la imediatamente. Todo o código que é executado dentro da função esta no escopo do closure (algo como “fechamento”), o que proporciona privacidade e estado durante toda a vida da aplicação.

Observe a () em torno da função anônima. Isto é requerido pela linguagem, uma vez que declarações que começam com a função de token são sempre considerados declarações de função. Incluir () cria uma expressão de função.

Global Import

JavaScript tem um recurso conhecido como implied globals (globals implícitas). Sempre que um nome é usado, o interpretador volta pela cadeia de escopo em busca de uma declaração de variável (var) para esse nome. Se nada for encontrado, a variável é assumida como global; se é usado em um assignmente, o global é criado se não existir. Isso significa que o uso ou a criação de variáveis ​​globais em um closure anônimo é fácil. Infelizmente, isso leva a código difícil de gerir, já que não é óbvio (para humanos) que as variáveis ​​são globais em um determinado arquivo.

Felizmente, o recurso de função anônima fornece uma alternativa fácil a isso. Ao passar globais como parâmetros para a função anônima, que os importa para o código. Eis um exemplo:

Module Export

Às vezes, você não quer apenas usar globais, mas quer declará-los. É possível fazer isso facilmente ao exportá-los, usando o valor de retorno da função anônima. Se o fizer, completará o Padrão de Módulo básico. Veja um exemplo completo:

Observe que foi declarado um módulo global chamado MODULE com duas propriedades públicas: um método chamado MODULE.moduleMethod e uma variável chamada MODULE.moduleProperty. Além disso, ele mantém o estado interno privada usando o closure da função anônima — e também é possível importar globais necessários usando o padrão mostrado anteriormente.

Padrões Avançados de Module Pattern

Os Padrões de Module Pattern mostrados anteriormente são suficientes para muitos casos, mas é possível levar isso adiante e criar alguns construtos muito poderosos e extensíveis. Vamos a eles, um por um, continuando com o módulo MODULE.

Augmentation

Uma limitação do Module Pattern até agora é que o módulo inteiro deve estar em um arquivo. Quem já trabalhou em uma grande base de código entende o valor de divisão de código em vários arquivos. Felizmente, existe uma boa solução: Augmentation (Ampliação). Em primeiro lugar, é preciso importar o módulo; então, são adicionadas novas propriedades e; por fim, ele é exportado.

Aqui está um exemplo de uso de Augmentation:

É usada a palavra-chave var novamente para consistência, apesar de não ser estritamente necessário. Após este código ser executado, o módulo terá ganho um novo método público chamado MODULE.anotherMethod. Este arquivo “ampliado” também manterá seu próprio estado interno privado e importações.

Loose Augmentation

Enquanto o exemplo acima requer a criação inicial do módulo em primeiro lugar e a ampliação acontecer depois disso, isso nem sempre é necessário. Uma das melhores coisas que um aplicativo JavaScript pode fazer em relação a desempenho é carregar scripts de forma assíncrona. É possível criar módulos flexíveis de várias partes que podem se carregar em qualquer ordem com o Loose Augmentation (Ampliação Fraca). Cada arquivo deve ter a seguinte estrutura:

Neste padrão, a instrução var é sempre necessária. Note que a importação vai criar o módulo se ele ainda não existir. Isto significa que você pode usar uma ferramenta como LABjs e carregar todos os seus arquivos de módulo em paralelo, sem a necessidade de bloquear.

Tight Augmentation

Tight Augmentation (Ampliação Forte) é excelente, mas coloca algumas limitações no módulo, dentre as quais se destaca a impossibilidade de poder substituir as propriedades do módulo de maneira segura. Também não é possível usar as propriedades do módulo de outros arquivos durante a inicialização — embora seja possível em tempo de execução após a inicialização. Loose Augmentation implica numa ordem estabelecida de carregamento, mas permite substituições.

Eis um exemplo simples (ampliando o módulo-exemplo original):

MODULE.moduleMethod foi substituído, mas manteve uma referência para o método original, caso fosse necessário.

Cloning and Inheritance (Clonagem e Herança)

Dentre os Padrões de Module Pattern, talvez este seja a opção mais flexível. Ele permite que algumas composições elegantes, mas que vem à custa da flexibilidadea: propriedades que são objetos ou funções não serão duplicadas e existirão como um objeto com duas referências. Mudando uma, mudará a outra. Isso pode ser corrigido para objetos com um processo de clonagem recursiva, mas, provavelmente, não pode ser fixado para funções — exceto, talvez, com eval().

Cross-File Private State (Estado Privado Entre-Arquivos)

Uma limitação grave de dividir um módulo em vários arquivos é que cada arquivo mantém seu próprio estado privado e não tem acesso ao estado privado dos outros arquivos. Mas isso pode ser corrigido.

Aqui está um exemplo de um módulo com Ampliação Fraca que vai manter o estado privado em todos as ampliações:

Qualquer arquivo pode definir propriedades em sua variável local _private e esta será imediatamente disponível para os outros. Uma vez que este módulo foi completamente carregado, o aplicativo deve chamar MODULE._seal(), que irá impedir o acesso externo ao interno _private.

Se este módulo fosse ampliado novamente, mais à frente na vida aplicativo um dos métodos internos, em qualquer arquivo, pode chamar _unseal() antes de carregar o novo arquivo e _seal() novamente depois de ter sido executado.

Submódulo

O padrão de Module Pattern Submódulo (Sub-module) é considerado pela maioria como o mais simples. Há muitos bons casos para a criação de submódulos. É como criar módulos regulares:

Embora isso possa parecer bastante óbvio, vale a pena ser mencionado em um artigo desse tipo, haja vista que submódulos têm todas as capacidades avançadas de módulos normais, incluindo ampliação e estado privado.

Conclusão

A maioria dos Padrões Avançados de Module Pattern podem ser combinados uns com os outros para criar padrões mais úteis. Não raramente é possível encontrar, em aplicações mais complexas, a combinação de Ampliação Fraca, Estado Privado e Submódulos.

No decorrer do artigo, não se mencionou a respeito de performance, mas uma nota rápida sobre isso seria: Module Pattern é bom para performance! Ele passa por minificação muito bem, o que faz com que o download seja mais rápido. Usar Loose Augmentation permite downloads paralelos, fáceis e sem bloqueios. O tempo de inicialização é, provavelmente, um pouco mais lento do que outros métodos, mas o custo-benefício vale a pena. Desempenho em tempo de execução não deve sofrer penalidades desde que globais sejam importados corretamente e, provavelmente, há aumento de velocidade em submódulos, encurtando a cadeia de referência com variáveis ​​locais.

Para fechar sobre Module Patterns, aqui está um exemplo de um submódulo que se carrega dinamicamente a seu parent (criando-se se ele não existe) — o estado privado foi deixado de fora para que o exemplo fique mais conciso, mas incluir isso seria simples.

Este padrão de código permite que toda uma base de códigos complexa hierárquica seja completamente carregada em paralelo, com sub-módulos e tudo o mais.

Apesar de alguns dos conceitos já terem sido apresentados no artigo artigo sobre JavaScript IIFE como contêiner de códigos, alguns usos avançados e macetes foram mostrados aqui, tornando possível constatar que JavaScript Module Pattern (ou Padrão de Módulo) pode fazer muita diferença na qualidade de seu código!