JavaScript–Module Pattern, Closures e Self-Executing AnonymousFunctions

Desing Patterns & JavaScript

capaJSMP

Quem trabalha com JavaScript conhece bem a problemática em relação ao escopo e criação de variáveis. Uma das grandes preocupações dos desenvolvedores JavaScript é evitar o uso indiscriminado de variáveis globais, o que pode levar a erros terríveis e de difícil rastreabilidade.

Como "AINDA" não temos uma sintaxe de módulos do próprio JavaScript, o padrão é a utilização de módulos para garantir um escopo de variáveis fechado, além de simular a privacidade de atributos e funções.

Este pattern pode envolver uma combinação de técnicas como closures e funções auto-executáveis. A sintaxe é bastante característica e pode ser encontrada facilmente em diversas bibliotecas, como no caso do WinJS.


(function () {
    // Seu código aqui!!!
})();

Clousures

Uma clousure define um determinado escopo de execução. Vamos ao exemplo:


function init() {
    console.log('Escopo Global');
}

(function () {
    function init() {
    console.log('Escopo Global');
    }

    init(); // Escopo Local

    window.init(); // Escopo Global
}());

fig1

Notem que aqui eu declarei 2 funções com o mesmo nome: init. Porém cada uma em um escopo. Uma está lá no escopo global window, e a outra é local dentro da clousure. Por existirem em escopos diferentes uma função não interfere na outra e pode coexistir em um mesmo código.

Tudo o que for declarado no escopo window é acessível de qualquer lugar, já que o escopo window é o root. O que for declarado dentro da clousure só é acessível de dentro dela, e se não for usado nenhum padrão de revelação só é acessível lá.

Se olharmos para o resultado do código veremos que primeiro foi executado a função do Escopo Local e depois a do Escopo Global sem erros.

Agora vamos ao código abaixo:


(function () {
    var namespaceUm = {
    init: function () {
    console.log('Namespace Um');
    }
    };

    // NamespaceUm
    namespaceUm.init();
}());
// ReferenceError: namespaceUm is not defined
namespaceUm.init();

fig2

Aqui vemos uma função declarada dentro de uma clousure e comprovamos como a mesma é inacessível fora deste contexto. No primeiro momento é executado a função que exibe o valor correto, e no segundo momento tento executar a função de fora do escopo da clousure o que gera o erro de objetjo undefined.

Isso é importante para entendermos como podemos criar módulos independentes e simulando a visibilidade privada tão difundida na OOP.

 

Módulos e Namespaces

No JavaScript tudo é objeto, isso inclui as funções. Sendo assim podemos atribuir uma função a uma variável. Quando pensamos na criação de um módulo ou namespace (você pode encontrar estes dois termos na literatura padrão), nós simplesmente pulamos esta atribuição, e de cara, já realizamos a execução da função assim que ela for definida.

Esta sintaxe é chamada de função anônima (Anonymous Functions). O porém da história é que não podemos executar uma função anônima, já que esta não é uma sintaxe válida. Sendo assim para realizar a auto-execução (Self Executing) da função, o correto é englobar tudo dentro de parênteses.



// Declarando uma função anônima que se auto executa
(function () {

})();

Como o JavaScript ainda não possui módulos nativamente (embora isso provavelmente entre na versão ECMAScript 6, também chamada de Harmony), este pattern é muito utilizado para simular a criação de módulos.

O que é uma função anônima? Fácil. É uma função sem um nome. NEXT!

Isso só é possível por causa do escopo local de variáveis de uma função, que permite isolar tudo o que é feito dentro deste tipo de função. Como variáveis definidas em uma função tem escopo local, podemos usar o Module Pattern para simular privacidade de atributos.

E por que isso é importante? Vamos a um exemplo prático. Observe o código abaixo:


var Pontos = {
    contador: 0,
    incrementar: function (){
    return this.contador += 1;
    },
    imprimir: function (){
    console.log(this.contador);
    }
};

Pontos.incrementar();
Pontos.incrementar();
Pontos.incrementar();
Pontos.imprimir(); // returns 3
Pontos.contador = 500;
Pontos.imprimir(); // returns 500

fig3

Temos uma função Pontos com um atributo contador responsável por guardar um valor que é importante em nosso regra. O ideal é que este valor só seja alterado pela utilização do método incrementar, porém como vemos no exemplo este tipo de implementação torna muito fácil a quebra do encapsulamento, dando total acesso a manipulação do atributo Pontos.contador de forma direta.

Para resolver este problema vamos criar uma variável local para armazenar o contador e o expor em uma interface pública que retorna esse valor.  Vamos ao código:


var Pontos = (function () {
    var contador = 0;
    return {
    contador: function () {
    return contador;
    },
    incrementar: function () {
    return contador += 1;
    },
    imprimir: function () {
    console.log(contador);
    }
    };
})();

Pontos.incrementar();
Pontos.incrementar();
Pontos.imprimir(); // returns 2
Pontos.contador = 500;
Pontos.imprimir(); // returns 2

fig4

Note que ambas as funções referenciam a variável contador, que agora não é mais acessível de forma direta, ou de fora do escopo da função auto executável. Mesmo que alguém tente alterar o valor da variável contador de fora do escopo, o valor continua o mesmo, só sendo alterado pelo método incrementar.

Dessa forma, podemos encapsular todo o comportamento, sem termos que nos preocupar com modificações feitas sem ser pela função Pontos.incrementar(), exatamente como o esperado.

Agora vamos para um exemplo do dia a dia. Observe o seguinte código e teste o seu resultado:

 

Em breve trago outros exemplos de implementações do modelo Self-Executing Anonymous Functions no JavaScript.

 

Referência

Learning JavaScript Design Patterns

 

Bons estudos e até a próxima pessoal  ;)


Author's profile picture

Vitor is a computer scientist who is passionate about creating software that will positively change the world we live in.

MVP Azure - Cloud Architect - Data science enthusiast


5 minutes to read