JavaScript IIFE como contêiner de códigos

11:21

Encontramos uma postagem interessante sobre Javascript IIFE no blog Desenvolvimento Para Web, que explica em muitos detalhes a sintaxe e as funcionalidades do IIFE. A equipe da Turbosite repassa esse conhecimento para os leitores do nosso blog, com a postagem a seguir.

 

JavaScript puro ou Vanilla JavaScript (felizmente) está voltando. E com força. Mas, quando você decide continuar nos estudos e começa a inspecionar códigos alheios, inevitavelmente você se depara com algo como:

(function (window, document, undefined) { //})(window, document);

E você encontra isso em diversos códigos; e vê outros desenvolvedores indicando; e colegas dizendo que esta é uma boa prática. Mas você sabe o que, realmente, isso significa? Continue lendo.

 

Frequentemente desenvolvedores descobrindo (ou redescobrindo) o JavaScript puro se perguntam sobre IIFE (Expressão de Função Imediatamente Invocada ou Immediately-Invoked Function Expression), frequentemente apresentado da seguinte maneira:

(function (window, document, undefined) { //})(window, document);

Na verdade, várias coisas estão acontecendo aí. Analisemos cada uma delas.

 

Escopo privado em IIFE

JavaScript tem maneiras interessantes de trabalhar com escopo de função; então, primeiramente essa expressão cria um “âmbito privado” (private scope). Por exemplo:

(function (window, document, undefined) { var name = 'Foo';})(window, document);console.log(name); // "name" não definido, está em um escopo diferente

 

Como funciona IIFE

Uma função normal se parece com isso:

var logMyName = function (name) { console.log(name);};logMyName('Foo');

Ela é chamada (invoke) quando necessário, por escolha do programador, em qualquer ponto do código em que se queira e/ou seja possível.

A razão pela qual essa IIFE em JavaScript foi criada, foi para ser uma expressão de função imediatamente invocada, o que significa que elas são imediatamente chamadas em tempo de execução – também, não é possível chamá-la novamente, já que elas só rodam uma vez, mais ou menos assim:

var logMyName = (function (name) { console.log(name); // Todd})('Todd');

O pulo-do-gato aqui é isso (que já foi atribuído a uma variável no exemplo anterior):

(function () {})();

O par de parênteses extra é necessário. Isso não funciona:

function () {}();

Embora existam vários truques que podem ser feitos para “enganar” o JavaScript e fazê-lo funcionar. Este, por exemplo, força o parser de JavaScript a tratar o código que segue ! como uma expressão:

!function () {}();

E existem variantes:

+function () {}(); -function () {}(); ~function () {}();

Mas não é preciso se forçar a decorar isso para usar a todo momento para fazer uma IIFE.

 

Argumentos da IIFE

Agora que foi explicado como isso funciona, passemos para os argumento da IIFE:

(function (window) {})(window);

Como isso funciona? Lembre-se, (window); é onde a função é chamada, e onde é passado o Objeto window. Então ele é passado para a função – que também foi chamada de window.

Então, o que mais é possível fazer? Passar tudo o que for necessário! Por exemplo, passar o Objeto document:

(function (window, document) { // é possível usar "window" e "document" normalmente})(window, document);

A resolução de variáveis ​​locais é mais rápida que a de variáveis ​​globais, mas esta está numa escala enorme e dificilmente alguém vai perceber o aumento da velocidade – mas vale a pena considerar se há muitas referências a globais!

 

E sobre o undefined?

Em ECMAScript 3, undefined é mutável. Isso significa que o valor poderia ser transferido com, por exemplo, undefined = true;…! Felizmente, no ECMAScript 5 em modo estrito ('use strict';) o parser lança um erro quando se depara com isso. Antes disso, começou-se a proteger uma IIFE da maneira já vista:

(function (window, document) { // é possível usar "window" e "document" normalmente})(window, document);
(function (window, document, undefined) {})(window, document);

O que significa que se alguém fizesse isso estaria tudo bem:

undefined = true;(function (window, document, undefined) { // "undefined" é uma variável local undefined})(window, document);

 

Minificando

Ao minificar as variáveis ​​locais é que a grandiosidade do padrão IIFE realmente pode ser vista. Nomes de variáveis ​​locais não são realmente necessários se estas foram passadas dessa maneira​​, então, é possível dar o nome que quiser para elas.

Por exemplo, mudar disso:

(function (window, document, undefined) { console.log(window); // Object window})(window, document);

Pra isso:

(function (a, b, c) { console.log(a); // Object window})(window, document);

Imagine todas as referências a bibliotecas e window e document minificadas. Claro que você não precisa parar por aí; é possível passar jQuery, também, ou qualquer outra coisa que esteja disponível dentro no escopo léxico:

(function ($, window, document, undefined) { // use $ para se referir à jQuery // $(document).addClass('test');})(jQuery, window, document); (function (a, b, c, d) { // Se torna // a(c).addClass('test');})(jQuery, window, document);

Isso também significa que você não precisa chamar jQuery.noConflict(); ou qualquer coisa como $ é atribuído localmente ao módulo. Aprender como escopos e variáveis ​​globais/locais funcionam ajuda ainda mais.

Passar o código por um bom minificador certamente garantirá a renomeação de undefined para c (por exemplo, e apenas se for usado). É importante atentar para o fato de que o nome undefined é irrelevante; só é preciso saber que o Objeto referenciado é indefinido (undefined) e, como undefined não tem qualquer significado especial – undefined é o valor que o JavaScript dá às coisas que são declaradas mas não têm nenhum valor (nem em IIFE).

 

Ambientes globais não-navegador

Devido a algumas tecnologias, Node.js, o navegador não é sempre o Objeto global, o que pode ser realmente problemático se você está tentando criar uma IIFE para funcionar em múltiplos ambientes. Por esta razão, há determinadas “alternativas”, como isso:

(function (root) {})(this);

Em um navegador, o ambiente global this se refere ao Objeto window, de modo que não é preciso passar em window, é sempre possível reduzir para this.

Alguns preferem o nome root, já que ele pode se referir tanto a ambientes não-navegador, quanto à raiz do browser.

Se você estiver interessado em uma solução universal (muito útil para ser usada para a criação de módulos em projetos de código aberto) é o UMD (Universal Module Definition):

(function (root, factory) { if (typeof define === 'function' && define.amd) { define(factory); } else if (typeof exports === 'object') { module.exports = factory; } else { root.MYMODULE = factory(); }})(this, function () { // });

A função está sendo invocado com outra função sendo passada para ela. É possível, então, atribuí-la ao ambiente relevante “de dentro”. No navegador, root.MYMODULE = factory(); é o módulo IIFE; em qualquer outro ambiente (como Node.js), será usado module.exports ou requireJS se define === 'function' && define.amd resolver como true.

 

Conclusão

Como foi visto, usar IIFE (Expressão de Função Imediatamente Invocada ou Immediately-Invoked Function Expression) é extramente benéfico. Além de ajudar na performance, por permitir que não seja preciso lookups no código a procura de variáveis globais, permite que seu código possa ficar “isolado”, protegendo o escopo de uma porção de código ou módulo do ambiente em que foi colocado.

O fato de diversos frameworks e bibliotecas JavaScript de renome como jQuery, Backbone.js, Modernizr e outros usarem IIFE já é bastante significativo. Use código de qualidade para fazer código de qualidade.