Posso nomear uma função JavaScript e executá-la imediatamente?

92

Eu tenho alguns destes:

function addEventsAndStuff() {
  // bla bla
}
addEventsAndStuff();

function sendStuffToServer() {
  // send stuff
  // get HTML in response
  // replace DOM
  // add events:
  addEventsAndStuff();
}

É necessário adicionar novamente os eventos porque o DOM foi alterado, então os eventos previamente anexados desapareceram. Uma vez que eles devem ser fixados inicialmente também (duh), eles estão em uma boa função para serem SECOS.

Não há nada de errado com essa configuração (ou há?), Mas posso suavizar um pouco? Gostaria de criar a addEventsAndStuff()função e chamá-la imediatamente, para que não pareça tão amadorística.

Ambos os seguintes respondem com um erro de sintaxe:

function addEventsAndStuff() {
  alert('oele');
}();

(function addEventsAndStuff() {
  alert('oele');
})();

Qualquer comprador?

Rudie
fonte
possível duplicata da função autoexecutável Javascript
Cristian Sanchez
2
Não, @CristianSanchez.
Máxima Alekz
Imho seu segundo bloco de código é difícil de ler. No primeiro bloco de código, fica claro para todos os leitores que você chama a função após o init. Os leitores tendem a ignorar o fechamento e a leitura excessiva dos colchetes no final.
kaiser

Respostas:

124

Não há nada de errado com o exemplo que você postou em sua pergunta. A outra maneira de fazer isso pode parecer estranha, mas:

var addEventsAndStuff;
(addEventsAndStuff = function(){
    // add events, and ... stuff
})();

Existem duas maneiras de definir uma função em JavaScript. Uma declaração de função:

function foo(){ ... }

e uma expressão de função, que é qualquer forma de definir uma função diferente da acima:

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

... etc

expressões de função podem ser nomeadas, mas seu nome não é propagado para o escopo contido. Significa que este código é válido:

(function foo(){
   foo(); // recursion for some reason
}());

mas isso não é:

(function foo(){
    ...
}());
foo(); // foo does not exist

Portanto, para nomear sua função e chamá-la imediatamente, você precisa definir uma variável local, atribuir sua função a ela como uma expressão e chamá-la.

Mark Kahn
fonte
1
"Expressões de função podem ser nomeadas" Cuidado com as expressões de função nomeadas. Eles deveriam funcionar como você descreveu, mas não funcionavam no IE antes do IE9 - você obtém duas funções e o nome da função vaza. Detalhes: kangax.github.com/nfe (Isso finalmente foi corrigido no IE9.)
TJ Crowder
@TJ - obrigado pela referência. Não percebi isso, pois nunca uso o IE, exceto para testar produtos finais :) Presumo que o IE9 não emula esse bug ao configurá-lo para o modo IE7 / 8? Acho que ainda usa o mecanismo JS do IE9 ... Meh
Mark Kahn
Se, como eu, você tiver problemas com o jshint com esse método, pode usar o call()método aqui: stackoverflow.com/a/12359154/1231001
cfx
Essa abordagem pode parecer útil em certas circunstâncias, mas além de ser um pouco mais difícil de ler, parece ter praticamente a mesma quantidade de texto.
Vladimir
17

Há um bom atalho para isso (não é necessário declarar nenhuma variável para além da atribuição da função):

var func = (function f(a) { console.log(a); return f; })('Blammo')
James Gorrie
fonte
Qual rserá a função de retorno? O function rou o var r? Apenas curioso. Boa solução. Não precisava ser um loc, embora =)
Rudie
Eu mudei o nome para ficar mais claro, mas return rteria sido o function rporque era seu escopo. Tenho escrito Scala tão permanentemente tentando colocar algo online.
James Gorrie
@JamesGorrie, abreviatura brilhante, no entanto, tentei duas vezes no console, parece que func, func (), func () () retornam a declaração de função de f (), mas podemos imprimir 'Blammo' por func () como esperado, por favor :)?
mike652638
@ mike652638 Eu executei isso no meu console e ele registra blammo?
James Gorrie
5

Não há nada de errado com essa configuração (ou há?), Mas posso suavizar um pouco?

Considere o uso da delegação de eventos. É onde você realmente observa o evento em um contêiner que não desaparece e, em seguida, usa event.target(ou event.srcElementno IE) para descobrir onde o evento realmente ocorreu e tratá-lo corretamente.

Dessa forma, você anexa o (s) manipulador (es) apenas uma vez e eles continuam funcionando mesmo quando você troca o conteúdo.

Aqui está um exemplo de delegação de evento sem usar nenhuma biblioteca auxiliar:

(function() {
  var handlers = {};

  if (document.body.addEventListener) {
    document.body.addEventListener('click', handleBodyClick, false);
  }
  else if (document.body.attachEvent) {
    document.body.attachEvent('onclick', handleBodyClick);
  }
  else {
    document.body.onclick = handleBodyClick;
  }

  handlers.button1 = function() {
    display("Button One clicked");
    return false;
  };
  handlers.button2 = function() {
    display("Button Two clicked");
    return false;
  };
  handlers.outerDiv = function() {
    display("Outer div clicked");
    return false;
  };
  handlers.innerDiv1 = function() {
    display("Inner div 1 clicked, not cancelling event");
  };
  handlers.innerDiv2 = function() {
    display("Inner div 2 clicked, cancelling event");
    return false;
  };

  function handleBodyClick(event) {
    var target, handler;

    event = event || window.event;
    target = event.target || event.srcElement;

    while (target && target !== this) {
      if (target.id) {
        handler = handlers[target.id];
        if (handler) {
          if (handler.call(this, event) === false) {
            if (event.preventDefault) {
              event.preventDefault();
            }
            return false;
          }
        }
      }
      else if (target.tagName === "P") {
        display("You clicked the message '" + target.innerHTML + "'");
      }
      target = target.parentNode;
    }
  }

  function display(msg) {
    var p = document.createElement('p');
    p.innerHTML = msg;
    document.body.appendChild(p);
  }

})();

Exemplo vivo

Observe como, se você clicar nas mensagens que são adicionadas dinamicamente à página, seu clique é registrado e manipulado, embora não haja nenhum código para conectar eventos nos novos parágrafos que estão sendo adicionados. Observe também como seus manipuladores são apenas entradas em um mapa, e você tem um manipulador no document.bodyque faz todo o despacho. Agora, você provavelmente enraíza em algo mais direcionado do que document.body, mas essa é a ideia. Além disso, no exemplo acima, basicamente estamos enviando por id, mas você pode fazer correspondências tão complexas ou simples quanto desejar.

Bibliotecas JavaScript modernas como jQuery , Prototype , YUI , Closure ou qualquer uma das várias outras devem oferecer recursos de delegação de eventos para amenizar as diferenças do navegador e lidar com casos extremos de forma limpa. jQuery certamente o faz, com suas funções livee delegate, que permitem especificar manipuladores usando uma gama completa de seletores CSS3 (e mais alguns).

Por exemplo, aqui está o código equivalente usando jQuery (exceto que tenho certeza de que jQuery lida com casos extremos que a versão bruta improvisada acima não):

(function($) {

  $("#button1").live('click', function() {
    display("Button One clicked");
    return false;
  });
  $("#button2").live('click', function() {
    display("Button Two clicked");
    return false;
  });
  $("#outerDiv").live('click', function() {
    display("Outer div clicked");
    return false;
  });
  $("#innerDiv1").live('click', function() {
    display("Inner div 1 clicked, not cancelling event");
  });
  $("#innerDiv2").live('click', function() {
    display("Inner div 2 clicked, cancelling event");
    return false;
  });
  $("p").live('click', function() {
    display("You clicked the message '" + this.innerHTML + "'");
  });

  function display(msg) {
    $("<p>").html(msg).appendTo(document.body);
  }

})(jQuery);

Cópia ao vivo

TJ Crowder
fonte
Não quero usar Event.target, porque esse nem sempre é o alvo certo. Está certo. Se você clicar em um IMGdentro de a DIVe DIVtiver o evento, Event.targetserá o IMGe não há como obter o DIVobjeto de maneira adequada. editar Btw eu odeio o .live'evento' do jQuery
Rudie
@Rudie: Re event.target: O que você descreve não está errado, está correto. Se você clicar em um imgdentro de a dive estiver assistindo a div, event.targeté o img(e thisé o div). Dê uma outra olhada no acima. Você pode anexar um manipulador em qualquer ponto da cadeia entre o elemento que foi clicado (o img, por exemplo) e o elemento que você está observando (acima, até abaixo document.body). Re live: Eu prefiro muito delegatea versão mais contida de live. Usei liveacima porque era o mais próximo do que fiz no exemplo bruto. Best,
TJ Crowder
4

Você pode querer criar uma função auxiliar como esta:

function defineAndRun(name, func) {
    window[name] = func;
    func();
}

defineAndRun('addEventsAndStuff', function() {
    alert('oele');
});
pimvdb
fonte
2
Por que essa solução é ruim? Porque as funções são registradas no namespace global? Quem votou contra isso?
Rudie
Ótima ideia
:)
4

Seu código contém um erro de digitação:

(function addEventsAndStuff() {
  alert('oele');
)/*typo here, should be }*/)();

então

(function addEventsAndStuff() {
  alert('oele');
 })();

trabalho. Felicidades!

[ editar ] com base no comentário: e isso deve ser executado e retornar a função de uma vez:

var addEventsAndStuff = (
 function(){
  var addeventsandstuff =  function(){
    alert('oele');
  };
  addeventsandstuff();
  return addeventsandstuff;
 }()
);
KooiInc
fonte
Suportes estúpidos são todos parecidos !! Obrigado! Saúde, de fato!
Rudie
Sim ... Então eh ... Na verdade, isso não funciona. A função é executada imediatamente, sim, mas não está registrada em nenhum lugar, então chamá-la novamente ->ReferenceError
Rudie
@Rudie: acabei de descobrir que o KomodoEdit finalmente faz tudo que eu sempre quis que o Aptana Studio fizesse (mas quebrava o tempo todo) e, além disso, é altamente configurável, rápido e repleto de complementos úteis. E destaca os colchetes correspondentes: você pode até dar a esses colchetes uma cor de advertência vermelha firme! Experimente, é grátis: activestate.com/komodo-edit
KooiInc
Cara ... (E obviamente eu digitei no editor da Web do SO, não em um editor de desktop.)
Rudie
E isso é muita digitação extra e lembrar o que e como digitar. Gostei da solução original (não funciona), mas a nova é muito difícil =)
Rudie
2

Ainda mais simples com ES6:

var result = ((a, b) => `${a} ${b}`)('Hello','World')
// result = "Hello World"
var result2 = (a => a*2)(5)
// result2 = 10
var result3 = (concat_two = (a, b) => `${a} ${b}`)('Hello','World')
// result3 = "Hello World"
concat_two("My name", "is Foo")
// "My name is Foo"
Alberto
fonte
Então, como eu chamaria de novo? Qual é o seu nome, para chamá-lo? Você está apenas mantendo o resultado, não a função.
Rudie
Este método é usado em alguns casos apenas se você precisar chamá-lo imediatamente e não quiser executá-lo novamente. De qualquer forma irei editar a resposta para mostrar como poder chamá-la de novo, o que você acha? @Rudie
Alberto
1
Minha pergunta era sobre nomear e executar novamente. concat_twofunciona perfeitamente, MAS sempre o define no âmbito global, então não vai funcionar "use strict". Isso não é importante para mim, mas é uma pequena desvantagem.
Rudie,
1

Se você deseja criar uma função e executá-la imediatamente -

// this will create as well as execute the function a()
(a=function a() {alert("test");})();

// this will execute the function a() i.e. alert("test")
a();
Praveen Lobo
fonte
1
Cuidado, quando você usa uma função nomeada como um valor à direita (como você faz acima), você está usando uma expressão de função nomeada que, embora deva ser válida, infelizmente tem problemas em várias implementações (principalmente - quelle shock - IE ) Detalhes: kangax.github.com/nfe
TJ Crowder
0

Tente fazer assim:

var addEventsAndStuff = (function(){
    var func = function(){
        alert('ole!');
    };
    func();
    return func;
})();
Alexander Ruliov
fonte
1
Isso está perto, mas var foo = (function() {...})();chama a função antes da atribuição. O parêntese deve incluir a atribuição. (Você deseja atribuir, em seguida, ligue.)
David
Isso também é muita digitação E lembrar o que digitar. Então prefiro usar minhas ligações atuais.
Rudie