JavaScript puro ou Vanilla JavaScript (felizmente) está voltando. E com força. Aqui, mesmo, no desenvolvimento para web, você consegue encontrar vários artigos mostrando como é possível fazer tudo o que você faz com bibliotecas de centenas de KB com poucas linhas de código.
Mas, quando você decide continuar nos estudos e começa a inspecionar códigos alheios, inevitavelmente você se depara com algo como:
|
1 2 3 |
(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:
|
1 2 3 |
(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:
|
1 2 3 4 5 |
(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:
|
1 2 3 4 5 |
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:
|
1 2 3 |
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):
|
1 2 3 |
(function () { })(); |
O par de parênteses extra é necessário. Isso não funciona:
|
1 2 3 |
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:
|
1 2 3 |
!function () { }(); |
E existem variantes:
|
1 2 3 4 5 6 7 8 9 10 11 |
+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:
|
1 2 3 |
(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:
|
1 2 3 |
(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:
|
1 2 3 |
(function (window, document) { // é possível usar "window" e "document" normalmente })(window, document); |
|
1 2 3 |
(function (window, document, undefined) { })(window, document); |
O que significa que se alguém fizesse isso estaria tudo bem:
|
1 2 3 4 |
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:
|
1 2 3 |
(function (window, document, undefined) { console.log(window); // Object window })(window, document); |
Pra isso:
|
1 2 3 |
(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:
|
1 2 3 4 5 6 7 8 9 |
(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:
|
1 2 3 |
(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):
|
1 2 3 4 5 6 7 8 9 10 11 |
(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.
Mas isso é assunto para outro artigo. Por enquanto, só dê uma olhada no repositório do UMD.
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.