Ultimamente, tenho lido muito Javascript e tenho notado que o arquivo inteiro está agrupado da seguinte forma nos arquivos .js a serem importados.
(function() {
...
code
...
})();
Qual é a razão para fazer isso, em vez de um simples conjunto de funções construtoras?
javascript
scope
coding-style
iife
Andrew Kou
fonte
fonte
Respostas:
Geralmente é para namespace (veja mais adiante) e controle a visibilidade de funções-membro e / ou variáveis. Pense nisso como uma definição de objeto. O nome técnico para ele é uma expressão de função chamada imediatamente chamada (IIFE). Os plugins jQuery geralmente são escritos assim.
Em Javascript, você pode aninhar funções. Portanto, o seguinte é legal:
Agora você pode ligar
outerFunction()
, mas a visibilidade deinnerFunction()
é limitada ao escopo deouterFunction()
, o que significa que é privadaouterFunction()
. Basicamente, segue o mesmo princípio que as variáveis em Javascript:Correspondentemente:
No cenário acima, você pode ligar
globalFunction()
de qualquer lugar, mas não pode ligarlocalFunction1
oulocalFunction2
.O que você está fazendo ao escrever
(function() { ... })()
é transformar o código dentro do primeiro conjunto de parênteses em uma função literal (o que significa que todo o "objeto" é na verdade uma função). Depois disso, você invoca automaticamente a função (a final()
) que acabou de definir. Portanto, a principal vantagem disso, como mencionei antes, é que você pode ter métodos / funções e propriedades particulares:No primeiro exemplo, você invocaria explicitamente
globalFunction
por nome para executá-lo. Ou seja, você faria apenasglobalFunction()
para executá-lo. Mas no exemplo acima, você não está apenas definindo uma função; você está definindo e invocando de uma só vez. Isso significa que, quando o arquivo JavaScript é carregado, ele é imediatamente executado. Claro, você poderia fazer:O comportamento seria basicamente o mesmo, exceto por uma diferença significativa: você evita poluir o escopo global ao usar um IIFE (como conseqüência, isso também significa que você não pode invocar a função várias vezes, pois ela não tem nome, mas como essa função deve ser executada apenas quando realmente não for um problema).
O mais interessante do IIFEs é que você também pode definir as coisas internas e apenas expor as partes que deseja para o mundo exterior (um exemplo de espaço para nome para que você possa basicamente criar sua própria biblioteca / plugin):
Agora você pode ligar
myPlugin.public_function1()
, mas não pode acessarprivate_function()
! Tão parecido com uma definição de classe. Para entender isso melhor, recomendo os seguintes links para algumas leituras adicionais:EDITAR
Eu esqueci de mencionar. Nessa final
()
, você pode passar o que quiser dentro. Por exemplo, quando você cria plugins jQuery, você passajQuery
ou$
assim:Então, o que você está fazendo aqui é definir uma função que aceite um parâmetro (chamado de
jQ
variável local e conhecida apenas por essa função). Então você invoca a função automaticamente e passa um parâmetro (também chamadojQuery
, mas este é do mundo exterior e uma referência ao próprio jQuery). Não há necessidade urgente de fazer isso, mas existem algumas vantagens:Descrevi anteriormente como essas funções são executadas automaticamente na inicialização, mas se elas são executadas automaticamente, quem está passando os argumentos? Essa técnica assume que todos os parâmetros necessários já estão definidos como variáveis globais. Portanto, se o jQuery já não estivesse definido como uma variável global, este exemplo não funcionaria. Como você pode imaginar, o que o jquery.js faz durante a inicialização é definir uma variável global 'jQuery', bem como sua variável global '$' mais famosa, que permite que esse código funcione após a inclusão do jQuery.
fonte
;(function(jQ) { ... code ... })(jQuery);
dessa maneira, se alguém deixasse um ponto e vírgula em seu script, não quebraria o seu, especialmente se você planeja minificar e concatenar seu script com outro.(function (context) { ..... })(this)
que lhe permite anexar o que quiser ao contexto pai, expondo-o.Em resumo
Sumário
Em sua forma mais simples, essa técnica visa agrupar o código dentro de um escopo de função .
Ajuda a diminuir as chances de:
Ele não detectar quando o documento está pronto - não é algum tipo de
document.onload
nemwindow.onload
É comumente conhecido como um
Immediately Invoked Function Expression (IIFE)
ouSelf Executing Anonymous Function
.Código explicado
No exemplo acima, qualquer variável definida na função (ou seja, declarada usando
var
) será "privada" e acessível somente dentro do escopo da função (como Vivin Paliath coloca). Em outras palavras, essas variáveis não são visíveis / alcançáveis fora da função. Veja demonstração ao vivo .Javascript tem escopo de função. "Parâmetros e variáveis definidos em uma função não são visíveis fora da função e que uma variável definida em qualquer lugar dentro de uma função é visível em qualquer lugar dentro da função." (de "Javascript: as boas partes").
Mais detalhes
Código Alternativo
No final, o código publicado anteriormente também pode ser feito da seguinte maneira:
Veja demonstração ao vivo .
As raízes
Iteração 1
Um dia, alguém provavelmente pensou "deve haver uma maneira de evitar nomear 'myMainFunction', já que tudo o que queremos é executá-lo imediatamente".
Se você voltar ao básico, descobrirá que:
expression
: algo avaliando um valor. ie3+11/x
statement
: linha (s) de código fazendo algo, mas não avalia como um valor. ieif(){}
Da mesma forma, as expressões de função são avaliadas para um valor. E uma conseqüência (presumo?) É que eles podem ser imediatamente invocados:
Portanto, nosso exemplo mais complexo se torna:
Veja demonstração ao vivo .
Iteração 2
O próximo passo é o pensamento "por que ter
var myMainFunction =
se nem sequer o usamos !?".A resposta é simples: tente remover isso, como abaixo:
Veja demonstração ao vivo .
Não funcionará porque "declarações de função não são invocáveis" .
O truque é que, removendo
var myMainFunction =
, transformamos a expressão da função em uma declaração de função . Consulte os links em "Recursos" para obter mais detalhes sobre isso.A próxima pergunta é "por que não posso mantê-lo como uma expressão de função com algo diferente
var myMainFunction =
?A resposta é "você pode" e, na verdade, existem várias maneiras de fazer isso: adicionar a
+
, a!
, a-
ou talvez colocar parênteses (como agora é feito por convenção) e muito mais, acredito. Como exemplo:ou
ou
Assim, depois que a modificação relevante é adicionada ao que era nosso "Código Alternativo", retornamos exatamente ao mesmo código que o usado no exemplo "Código Explicado"
Leia mais sobre
Expressions vs Statements
:Escopos de desmistificação
Uma coisa que podemos nos perguntar é "o que acontece quando você NÃO define a variável 'adequadamente' dentro da função - ou seja, faz uma atribuição simples?"
Veja demonstração ao vivo .
Basicamente, se uma variável que não foi declarada em seu escopo atual tiver um valor atribuído, "uma pesquisa na cadeia de escopo ocorrerá até encontrar a variável ou atingir o escopo global (no momento em que será criada)".
Quando em um ambiente de navegador (versus um ambiente de servidor como nodejs), o escopo global é definido pelo
window
objeto. Por isso, podemos fazerwindow.myOtherFunction()
.Minha dica "Boas práticas" sobre este tópico é sempre usar
var
ao definir algo : seja um número, objeto ou função e até mesmo no escopo global. Isso torna o código muito mais simples.Nota:
block scope
(Atualização: variáveis locais do escopo do bloco adicionadas no ES6 .)function scope
&global scope
(window
escopo em um ambiente de navegador)Leia mais sobre
Javascript Scopes
:Recursos
Próximos passos
Depois de obter esse
IIFE
conceito, ele leva aomodule pattern
, o que geralmente é feito ao alavancar esse padrão IIFE. Diverta-se :)fonte
Javascript em um navegador realmente tem apenas alguns escopos efetivos: escopo de função e escopo global.
Se uma variável não está no escopo da função, está no escopo global. E variáveis globais são geralmente ruins, portanto, isso é uma construção para manter as variáveis de uma biblioteca para si.
fonte
Isso é chamado de fechamento. Basicamente, sela o código dentro da função para que outras bibliotecas não interfiram nele. É semelhante à criação de um espaço para nome em idiomas compilados.
Exemplo. Suponha que eu escreva:
Agora outras bibliotecas não podem acessar a variável que
x
eu criei para usar na minha biblioteca.fonte
(function(){ ... return { publicProp1: 'blah' }; })();
. Obviamente, não é perfeitamente paralelo ao namespacing, mas pode ajudar a pensar dessa maneira.Você também pode usar fechamentos de função como dados em expressões maiores, como neste método para determinar o suporte ao navegador para alguns dos objetos html5.
fonte
Além de manter as variáveis locais, um uso muito útil é ao escrever uma biblioteca usando uma variável global, você pode atribuir um nome de variável mais curto a ser usado na biblioteca. É frequentemente usado na criação de plugins jQuery, pois o jQuery permite desativar a variável $ que aponta para o jQuery, usando jQuery.noConflict (). Caso esteja desabilitado, seu código ainda pode usar $ e não quebrar se você apenas fizer:
fonte
fonte
Também devemos usar 'use strict' na função scope para garantir que o código seja executado no "modo estrito". Código de exemplo mostrado abaixo
fonte