Por que você precisa invocar uma função anônima na mesma linha?

374

Eu estava lendo alguns posts sobre fechamentos e vi isso em todos os lugares, mas não há uma explicação clara de como funciona - sempre que me disseram para usá-lo ...:

// Create a new anonymous function, to use as a wrapper
(function(){
    // The variable that would, normally, be global
    var msg = "Thanks for visiting!";

    // Binding a new function to a global object
    window.onunload = function(){
        // Which uses the 'hidden' variable
        alert( msg );
    };
// Close off the anonymous function and execute it
})();

Ok, vejo que vamos criar uma nova função anônima e depois executá-la. Então, depois disso, esse código simples deve funcionar (e funciona):

(function (msg){alert(msg)})('SO');

Minha pergunta é que tipo de mágica acontece aqui? Eu pensei que quando escrevi:

(function (msg){alert(msg)})

então uma nova função sem nome seria criada como a função "" (msg) ...

mas então por que isso não funciona?

(function (msg){alert(msg)});
('SO');

Por que ele precisa estar na mesma linha?

Você poderia me indicar algumas postagens ou me dar uma explicação?

palig
fonte
2
Em outros idiomas, eles são chamados de Ponteiros de Função ou Delegados, se você quiser examinar as estruturas de nível inferior envolvidas.
22611 Chris Moschini
17
Você tem um ; na primeira linha
Oliver Ni
Agora que você sabe como funciona ... Não use. Deveríamos parar de escrever funções anônimas . Com apenas mais alguns caracteres, podemos dar um nome real às nossas funções e tornar a depuração do código Javascript muito mais fácil!
Stijn de Witt
11
A linha (function (msg){alert(msg)})('SO');funciona completamente por conta própria. Não tem nada a ver com a outra função anônima que você postou antes dela. Essas são duas funções anônimas completamente separadas. Você precisa invocar uma função anônima imediatamente porque ela não tem nome e não pode ser referenciada posteriormente.
Oct

Respostas:

380

Solte o ponto e vírgula após a definição da função.

(function (msg){alert(msg)})
('SO');

Acima deve funcionar.

Página DEMO: https://jsfiddle.net/e7ooeq6m/

Eu discuti esse tipo de padrão neste post:

jQuery e $ perguntas

EDITAR:

Se você observar as especificações de script do ECMA , existem três maneiras de definir uma função. (Página 98, Seção 13, Definição de função)

1. Usando o construtor Function

var sum = new Function('a','b', 'return a + b;');
alert(sum(10, 20)); //alerts 30

2. Usando declaração de função.

function sum(a, b)
{
    return a + b;
}

alert(sum(10, 10)); //Alerts 20;

3. Expressão de Função

var sum = function(a, b) { return a + b; }

alert(sum(5, 5)); // alerts 10

Então você pode perguntar: qual é a diferença entre declaração e expressão?

Da especificação de script ECMA:

FunctionDeclaration: identificador da função (FormalParameterListopt) {FunctionBody}

FunctionExpression: função Identifieropt (FormalParameterListopt) {FunctionBody}

Se você perceber, 'identificador' é opcional para a expressão da função. E quando você não fornece um identificador, você cria uma função anônima. Isso não significa que você não pode especificar um identificador.

Isso significa que seguir é válido.

var sum = function mySum(a, b) { return a + b; }

Um ponto importante a ser observado é que você pode usar 'mySum' apenas dentro do corpo da função mySum, não fora. Veja o seguinte exemplo:

var test1 = function test2() { alert(typeof test2); }

alert(typeof(test2)); //alerts 'undefined', surprise! 

test1(); //alerts 'function' because test2 is a function.

Demonstração ao vivo

Compare isso com

 function test1() { alert(typeof test1) };

 alert(typeof test1); //alerts 'function'

 test1(); //alerts 'function'

Armado com esse conhecimento, vamos tentar analisar seu código.

Quando você tem código como,

    function(msg) { alert(msg); }

Você criou uma expressão de função. E você pode executar esta expressão de função envolvendo-a entre parênteses.

    (function(msg) { alert(msg); })('SO'); //alerts SO.
SolutionYogi
fonte
11
Sim, mas porque? Por que precisa ser em linha? Não importa quantos espaços em branco eu usarei.
palig 16/07/2009
9
Como escrevi, o ponto-e-vírgula encerrou a definição de função anônima. Como não tem nome (é anônimo, duh!), Você não poderá mais chamá-lo. Se você não colocar ponto e vírgula, a função ainda poderá ser executada.
SolutionYogi
Eu pensei que a inserção automática de ponto e vírgula colocaria um ponto e vírgula nesse caso, mas isso não acontece. Então você está certo.
Nosredna
11
Nosredna, JS se comporta pouco arbitrariamente quando se trata de adicionar ponto e vírgula. Leia este artigo detalhado: blog.boyet.com/blog/javascriptlessons/…
SolutionYogi
Sim, vejo que (function (msg) {alert (msg)}) ('SO'); trabalho. Eu só estava perguntando por que isso funciona. Onde isso é especificado ou que tipo de recurso JS é esse. Então, uma vez que eu chamo: (function (msg) {alert (msg)}) o que acontecerá com a função? Será GC'ed?
palig 16/07/2009
129

É chamada de função auto-invocada.

O que você está fazendo quando chama (function(){})retornando um objeto de função. Quando você anexa ()a ele, ele é chamado e qualquer coisa no corpo é executada. A ;denota o fim da instrução, é por isso que a segunda invocação falha.

seth
fonte
Ah ok, entendo, então é apenas uma sintaxe especial do JS, certo? Como esta explicação mais! Simples e curto :)
palig
Eu acho incorreto dizer que o corpo será 'avaliado'. Ele executa como qualquer outra função. Por ser anônimo, você pode salvar a referência em algum lugar OU executá-la imediatamente.
SolutionYogi
16
Pessoalmente, eu nem gosto do termo 'função auto-invocável'. Não é que a função esteja se invocando. O programador escreveu esses parênteses para invocá-lo.
SolutionYogi
Não é "sintaxe especial" mais do que qualquer outra coisa é especial. Na verdade, o formulário "nome da função (args) {BLOCK}" é muito mais "especial". Na verdade, é açúcar desnecessário; é isso que realmente faz as coisas acontecerem.
jrockway
2
bom link para o artigo. Ele observa por que alguém usaria o seguinte: "Em um esforço para proteger o objeto global, todos os aplicativos JavaScript devem ser gravados em uma função auto-invocável. Isso criará um escopo de aplicativo no qual as variáveis ​​podem ser criadas sem o medo de colidir. com outras aplicações ". E também observou: "Quando a função termina, as variáveis ​​são descartadas e o objeto global permanece inalterado".
yeahdixon
94

Uma coisa que achei confusa é que os "()" são operadores de agrupamento.

Aqui está sua função declarada básica.

Ex. 1:

var message = 'SO';

function foo(msg) {
    alert(msg);
}

foo(message);

Funções são objetos e podem ser agrupados. Então, vamos jogar parens em torno da função.

Ex. 2:

var message = 'SO';

function foo(msg) {  //declares foo
    alert(msg);
}

(foo)(message);     // calls foo

Agora, em vez de declarar e chamar imediatamente a mesma função, podemos usar a substituição básica para declará-la como a chamamos.

Ex. 3)

var message = 'SO';

(function foo(msg) {
    alert(msg);
})(message);          // declares & calls foo

Finalmente, não precisamos desse foo extra porque não estamos usando o nome para chamá-lo! Funções podem ser anônimas.

Ex. 4)

var message = 'SO';

(function (msg) {   // remove unnecessary reference to foo
    alert(msg);
})(message);

Para responder à sua pergunta, consulte o Exemplo 2. Sua primeira linha declara alguma função sem nome e a agrupa, mas não a chama. A segunda linha agrupa uma sequência. Ambos não fazem nada. (Primeiro exemplo de Vincent.)

(function (msg){alert(msg)});  
('SO');                       // nothing.

(foo); 
(msg); //Still nothing.

Mas

(foo)
(msg); //works
Benxamin
fonte
6
Obrigado. Seus exemplos foram bem claros. Eu não sabia que os parênteses em JavaScript poderiam alterar o significado do código dessa maneira. Como sou de Java, aprendo algo novo (e muitas vezes inesperado) sobre JavaScript quase todos os dias que o uso.
precisa saber é o seguinte
5
Obrigado por fazê-lo passo a passo, isso é muito melhor do que qualquer outra explicação que eu já tenha visto. +1
Wk_of_Angmar
2
Momento importante da AHA aqui - e obrigado por ilustrar com substituição. +100
FredTheWebGuy
11
Uma das melhores explicações que li sobre funções anônimas. Muito obrigado!
Teknotica
23

Uma função anônima não é uma função com o nome "". É simplesmente uma função sem nome.

Como qualquer outro valor em JavaScript, uma função não precisa de um nome para ser criado. Embora seja muito mais útil vinculá-lo a um nome como qualquer outro valor.

Mas, como qualquer outro valor, às vezes você deseja usá-lo sem vinculá-lo a um nome. Esse é o padrão de auto-invocação.

Aqui está uma função e um número, não vinculados, eles não fazem nada e nunca podem ser usados:

function(){ alert("plop"); }
2;

Portanto, temos que armazená-los em uma variável para poder usá-los, assim como qualquer outro valor:

var f = function(){ alert("plop"); }
var n = 2;

Você também pode usar açúcar sintático para vincular a função a uma variável:

function f(){ alert("plop"); }
var n = 2;

Mas se não for necessário nomeá-los e levar a mais confusão e menos legibilidade, você poderá usá-los imediatamente.

(function(){ alert("plop"); })(); // will display "plop"
alert(2 + 3); // will display 5

Aqui, minha função e meus números não estão vinculados a uma variável, mas ainda podem ser usados.

Dito assim, parece que a função auto-invocadora não tem valor real. Mas você deve ter em mente que o delimitador de escopo do JavaScript é a função e não o bloco ({}).

Portanto, uma função de auto-chamada realmente tem o mesmo significado que um bloco C ++, C # ou Java. O que significa que a variável criada dentro não "vazará" fora do escopo. Isso é muito útil em JavaScript para não poluir o escopo global.

Vincent Robert
fonte
Bela postagem. O que acontecerá com a função '() {alert ("plop"); } 'quando eu o executei? Será GC'ed?
palig 16/07/2009
2
A função () {alert ("plop"); A instrução} apenas aloca a função, mas não a executa nem a vincula a uma variável. Como a função criada não está vinculada a nenhuma variável, ela será rapidamente submetida a GC.
24720 Vincent Robert
Esse encadeamento SO vai além do escopo do que estamos falando aqui, mas explica maneiras de separar os espaços para nome JavaScript - e inclui exemplos que usam funções de auto-chamada.
precisa saber é o seguinte
19

É assim que o JavaScript funciona. Você pode declarar uma função nomeada:

function foo(msg){
   alert(msg);
}

E chame:

foo("Hi!");

Ou, você pode declarar uma função anônima:

var foo = function (msg) {
    alert(msg);
}

E chame isso:

foo("Hi!");

Ou, você nunca pode vincular a função a um nome:

(function(msg){
   alert(msg);
 })("Hi!");

Funções também podem retornar funções:

function make_foo() {
    return function(msg){ alert(msg) };
}

(make_foo())("Hi!");

Não vale nada que quaisquer variáveis ​​definidas com "var" no corpo de make_foosejam fechadas por cada função retornada por make_foo. Este é um fechamento e significa que qualquer alteração feita no valor por uma função será visível por outra.

Isso permite que você encapsule informações, se desejar:

function make_greeter(msg){
    return function() { alert(msg) };
}

var hello = make_greeter("Hello!");

hello();

É exatamente como quase todas as linguagens de programação, exceto Java, funcionam.

jrockway
fonte
8

O código que você mostra,

(function (msg){alert(msg)});
('SO');

consistem em duas declarações. A primeira é uma expressão que gera um objeto de função (que será então coletado como lixo porque não é salvo). A segunda é uma expressão que produz uma string. Para aplicar a função à string, você precisa passar a string como argumento para a função quando ela é criada (que você também mostra acima), ou será necessário realmente armazenar a função em uma variável, para que você possa aplique-o posteriormente, à sua vontade. Igual a:

var f = (function (msg){alert(msg)});
f('SO');

Observe que, armazenando uma função anônima (uma função lambda) em uma variável, você está efetivamente dando um nome a ela. Portanto, você também pode definir uma função regular:

function f(msg) {alert(msg)};
f('SO');
Stephan202
fonte
7

Em resumo dos comentários anteriores:

function() {
  alert("hello");
}();

quando não atribuído a uma variável, gera um erro de sintaxe. O código é analisado como uma instrução de função (ou definição), que torna os parênteses de fechamento sintaticamente incorretos. Adicionar parênteses em torno da parte da função informa ao intérprete (e programador) que esta é uma expressão (ou chamada) de função, como em

(function() {
  alert("hello");
})();

Essa é uma função de auto-chamada, o que significa que é criada anonimamente e é executada imediatamente porque a chamada ocorre na mesma linha em que é declarada. Esta função auto-invocando é indicado com a sintaxe familiar para chamar uma função sem argumentos, além de parênteses adicionado ao redor do nome da função: (myFunction)();.

uma boa sintaxe da função JavaScript da discussão sobre SO .

hotshot309
fonte
3

Esta resposta não está estritamente relacionada à pergunta, mas você pode estar interessado em descobrir que esse tipo de recurso de sintaxe não é específico para funções. Por exemplo, sempre podemos fazer algo assim:

alert(
    {foo: "I am foo", bar: "I am bar"}.foo
); // alerts "I am foo"

Relacionado a funções. Como eles são objetos, que herdam do Function.prototype, podemos fazer coisas como:

Function.prototype.foo = function () {
    return function () {
        alert("foo");
    };
};

var bar = (function () {}).foo();

bar(); // alerts foo

E você sabe, nem precisamos rodear funções entre parênteses para executá-las. De qualquer forma, desde que tentemos atribuir o resultado a uma variável.

var x = function () {} (); // this function is executed but does nothing

function () {} (); // syntax error

Outra coisa que você pode fazer com as funções, assim que as declara, é invocar o newoperador sobre elas e obter um objeto. Os seguintes são equivalentes:

var obj = new function () {
    this.foo = "bar";
};

var obj = {
    foo : "bar"
};
Ionuț G. Stan
fonte
3

Há mais uma propriedade que a função JavaScript possui. Se você deseja chamar a mesma função anônima recursivamente.

(function forInternalOnly(){

  //you can use forInternalOnly to call this anonymous function
  /// forInternalOnly can be used inside function only, like
  var result = forInternalOnly();
})();

//this will not work
forInternalOnly();// no such a method exist
Anoop
fonte
2
+1 Adicionou uma pequena amostra para ficar mais clara :-) Na primeira vez que li, tive que reler 4 vezes.
Xanatos
3

Meu entendimento da pergunta do autor da pergunta é tal que:

Como essa mágica funciona:

(function(){}) ('input')   // Used in his example

Eu posso estar errado. No entanto, a prática usual com a qual as pessoas estão familiarizadas é:

(function(){}('input') )

A razão é tal que o JavaScript entre parênteses AKA () , não pode conter instruções e, quando o analisador encontra a palavra-chave function, ele sabe analisá-la como uma expressão de função e não como uma declaração de função.

Fonte: postagem no blog Expressão de Função Imediatamente Invocada (IIFE)

laycat
fonte
3

exemplos sem colchetes:

void function (msg) { alert(msg); }
('SO');

(este é o único uso real de void, afaik)

ou

var a = function (msg) { alert(msg); }
('SO');

ou

!function (msg) { alert(msg); }
('SO');

trabalhar também. a voidestá a causar a expressão para avaliar, assim como a atribuição e a explosão. o último trabalha com ~, +, -, delete, typeof, alguns dos operadores unários ( voidé um bem). não está trabalhando ++, --por causa da exigência de uma variável.

a quebra de linha não é necessária.

Nina Scholz
fonte
@ Bergi no ie11 deletefunciona. mesmo com 'use strict';. isso também funciona:delete (3 + 4);
Nina Scholz
Opa, meu erro. " 2) Se Type (ref) não for Reference, retorne true. " Ele gera apenas erros para referências reais que não podem ser resolvidas.
Bergi
1

É uma função anônima de execução automática. O primeiro conjunto de colchetes contém as expressões a serem executadas e o segundo conjunto de colchetes executa essas expressões.

(function () {
    return ( 10 + 20 );
})();

Peter Michaux discute a diferença em Um Par Importante de Parênteses .

É uma construção útil ao tentar ocultar variáveis ​​do namespace pai. Todo o código dentro da função está contido no escopo privado da função, o que significa que não pode ser acessado de fora da função, tornando-a verdadeiramente privada.

Vejo:

  1. Encerramento (ciência da computação)
  2. Namespacing JavaScript
  3. Par importante de parênteses Javascript
Ashwin Parmar
fonte
0

Outro ponto de vista

Primeiro, você pode declarar uma função anônima:

var foo = function(msg){
 alert(msg);
}

Então você chama:

foo ('Few');

Porque foo = function (msg) {alert (msg);} para que você possa substituir foo como:

function(msg){
 alert(msg);
} ('Few');

Mas você deve agrupar toda a função anônima dentro de par de chaves para evitar erros de sintaxe da declaração de função ao analisar. Então nós temos,

(function(msg){
 alert(msg);
}) ('Few');

Dessa forma, é fácil entender para mim.

capu
fonte
0

Quando você fez:

(function (msg){alert(msg)});
('SO');

Você encerrou a função antes ('SO')por causa do ponto e vírgula. Se você acabou de escrever:

(function (msg){alert(msg)})
('SO');

Vai funcionar.

Exemplo de trabalho: http://jsfiddle.net/oliverni/dbVjg/

Oliver Ni
fonte
0

A simples razão pela qual não funciona não é por causa da ;indicação do fim da função anônima. Isso ocorre porque, sem o ()término de uma chamada de função, não é uma chamada de função. Isso é,

function help() {return true;}

Se você ligar, result = help();é uma chamada para uma função e retornará true.

Se você ligar result = help; não é uma ligação. É uma tarefa em que a ajuda é tratada como dados a serem atribuídos ao resultado.

O que você fez foi declarar / instanciar uma função anônima adicionando o ponto-e-vírgula,

(function (msg) { /* Code here */ });

e tentei chamá-lo em outra instrução usando apenas parênteses ... Obviamente, porque a função não tem nome, mas isso não funcionará:

('SO');

O intérprete vê os parênteses na segunda linha como uma nova instrução / instrução e, portanto, não funciona, mesmo que você tenha feito assim:

(function (msg){/*code here*/});('SO');

Ainda não funciona, mas funciona quando você remove o ponto-e-vírgula porque o intérprete ignora os espaços em branco e os carros e vê o código completo como uma instrução.

(function (msg){/*code here*/})        // This space is ignored by the interpreter
('SO');

Conclusão: uma chamada de função não é uma chamada de função sem a ()extremidade, a menos que sob condições específicas, como ser invocada por outra função, ou seja, onload = 'help' execute a função help mesmo que os parênteses não tenham sido incluídos. Eu acredito que setTimeout e setInterval também também permitem esse tipo de chamada de função, e também acredito que o intérprete adiciona os parênteses nos bastidores de qualquer maneira, o que nos leva de volta a "uma chamada de função não é uma chamada de função sem os parênteses".

técnicas contratadas
fonte
Não entendo por que isso recebeu tantos votos negativos. Eu acho que é uma resposta aceitável? : /
Daniel Cheung
0
(function (msg){alert(msg)})
('SO');

Esse é um método comum de usar uma função anônima como um fechamento usado por muitas estruturas JavaScript.

Essa função chamada é automaticamente quando o código é compilado.

Se colocar ;na primeira linha, o compilador tratou como duas linhas diferentes. Portanto, você não pode obter os mesmos resultados acima.

Isso também pode ser escrito como:

(function (msg){alert(msg)}('SO'));

Para mais detalhes, consulte JavaScript / Funções Anônimas .

user2349539
fonte
Tanto quanto eu sei, JavaScript não "compilação"
Daniel Cheung
0
  1. Funções anônimas são declaradas dinamicamente em tempo de execução. Eles são chamados de funções anônimas porque não recebem um nome da mesma maneira que as funções normais.

    Funções anônimas são declaradas usando o operador function em vez da declaração da função. Você pode usar o operador de função para criar uma nova função sempre que for válido colocar uma expressão. Por exemplo, você pode declarar uma nova função como parâmetro para uma chamada de função ou atribuir uma propriedade de outro objeto.

    Aqui está um exemplo típico de uma função nomeada:

    função flyToTheMoon () {alert ("Zoom! Zoom! Zoom!"); } flyToTheMoon (); Aqui está o mesmo exemplo criado como uma função anônima:

    var flyToTheMoon = function () {alert ("Zoom! Zoom! Zoom!"); } flyToTheMoon ();

    Para detalhes, leia aqui:

    http://helephant.com/2008/08/23/javascript-anonymous-functions/

Harikesh Yadav
fonte
0

O IIFE simplesmente compartimenta a função e oculta a msgvariável para não "poluir" o espaço para nome global. Na realidade, mantenha-o simples e faça o seguinte, a menos que você esteja construindo um site de um bilhão de dólares.

var msg = "later dude";
window.onunload = function(msg){
  alert( msg );
};

Você pode namespace sua msgpropriedade usando um padrão de módulo revelador, como:

var myScript = (function() {
    var pub = {};
    //myscript.msg
    pub.msg = "later dude";
    window.onunload = function(msg) {
        alert(msg);
    };
    //API
    return pub;
}());
Ronnie Royston
fonte
-1

As funções anônimas devem ser tratadas de uma só vez, onde você define uma função em tempo real para gerar uma saída de você a partir de uma entrada que você está fornecendo. Exceto que você não forneceu a entrada. Em vez disso, você escreveu algo na segunda linha ('SO'); - uma declaração independente que não tem nada a ver com a função. O que você esperava? :)

Vietnhi Phuvan
fonte
Não é 100% correto. Esta é uma função anônima bem e se destina a ser reutilizado: var foo = function() {};. Tudo o resto está bem.
Felix Kling