Posso nominare una funzione JavaScript ed eseguirla immediatamente?


92

Ne ho alcuni:

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

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

È necessario aggiungere nuovamente gli eventi perché il DOM è cambiato, quindi gli eventi allegati in precedenza non sono più disponibili. Dal momento che devono essere attaccati anche inizialmente (duh), sono in una bella funzione per essere ASCIUTTI.

Non c'è niente di sbagliato in questa configurazione (o c'è?), Ma posso smussarla un po '? Vorrei creare la addEventsAndStuff()funzione e chiamarla immediatamente, quindi non sembra così amatoriale.

Entrambi i seguenti rispondono con un errore di sintassi:

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

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

Eventuali acquirenti?



2
No, @CristianSanchez.
Máxima Alekz

Imho il tuo secondo blocco di codice è difficile da leggere. Nel primo blocco di codice è chiaro per ogni lettore che si chiama la funzione dopo init. I lettori tendono a ignorare la chiusura e la lettura eccessiva delle parentesi alla fine.
Kaiser

Risposte:


124

Non c'è niente di sbagliato nell'esempio che hai pubblicato nella tua domanda .. L'altro modo di farlo potrebbe sembrare strano, ma:

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

Esistono due modi per definire una funzione in JavaScript. Una dichiarazione di funzione:

function foo(){ ... }

e un'espressione di funzione, che è un modo qualsiasi per definire una funzione diversa da quella sopra:

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

...eccetera

le espressioni di funzione possono essere denominate, ma il loro nome non viene propagato all'ambito contenitore. Significato questo codice è valido:

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

ma questo non è:

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

Quindi, per dare un nome alla tua funzione e chiamarla immediatamente, devi definire una variabile locale, assegnarle la tua funzione come un'espressione, quindi chiamarla.


1
"le espressioni di funzione possono essere denominate" Attenzione alle espressioni di funzione con nome. Essi dovrebbero lavorare come lei, ma non lo fanno su IE prima di IE9 - si ottiene due funzioni, e le perdite nome della funzione. Dettagli: kangax.github.com/nfe (Questo è stato finalmente risolto in IE9.)
TJ Crowder,

@TJ - grazie per il riferimento. Non me ne rendevo conto visto che non uso mai IE tranne che per testare i prodotti finali :) Presumo che IE9 non emuli questo bug quando lo imposto in modalità IE7 / 8? Penso che utilizzi ancora il motore IE9 JS ... Meh
Mark Kahn

Se, come me, si esegue in problemi jshint con questo metodo, è possibile utilizzare il call()metodo di da qui: stackoverflow.com/a/12359154/1231001
CFX

Questo approccio può sembrare utile in determinate circostanze, ma oltre ad essere un po 'più difficile da leggere, sembra avere praticamente la stessa quantità di testo.
Vladimir

17

C'è una buona scorciatoia per questo (non è necessario dichiarare alcuna variabile a barre dell'assegnazione della funzione):

var func = (function f(a) { console.log(a); return f; })('Blammo')

Quale rsarà la funzione restituita? Il function ro il var r? Solo curioso. Buona soluzione. Non doveva essere una locomotiva però =)
Rudie

L'ho rinominato per essere più chiaro, ma return rsarebbe stato il modo function rin cui era il suo scopo. Ho scritto Scala così costantemente cercando di ottenere qualcosa in linea.
James Gorrie

@JamesGorrie, Brilliant shorthand, tuttavia, ho provato due volte in console, sembra che func, func (), func () () restituiscano tutte la dichiarazione di funzione di f (), ma possiamo produrre 'Blammo' da func () come previsto per favore :)?
mike652638

@ mike652638 L'ho eseguito nella mia console e la console registra blammo?
James Gorrie

5

Non c'è niente di sbagliato in questa configurazione (o c'è?), Ma posso lisciarla un po '?

Guarda invece l'utilizzo della delega degli eventi. È lì che si guarda effettivamente l'evento su un contenitore che non scompare, quindi si utilizza event.target(o event.srcElementsu IE) per capire dove si è effettivamente verificato l'evento e gestirlo correttamente.

In questo modo, colleghi i gestori solo una volta e continuano a funzionare anche quando cambi il contenuto.

Ecco un esempio di delega degli eventi senza utilizzare alcuna libreria di supporto:

(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);
  }

})();

Esempio dal vivo

Nota come se fai clic sui messaggi che vengono aggiunti dinamicamente alla pagina, il tuo clic viene registrato e gestito anche se non è presente alcun codice per agganciare gli eventi ai nuovi paragrafi aggiunti. Nota anche come i tuoi gestori sono solo voci in una mappa, e hai un gestore su document.bodyche fa tutto il dispacciamento. Ora, probabilmente lo radichi in qualcosa di più mirato di document.body, ma hai l'idea. Inoltre, in quanto sopra stiamo fondamentalmente spedendo da id, ma puoi fare corrispondenze complesse o semplici come preferisci.

Le moderne librerie JavaScript come jQuery , Prototype , YUI , Closure o molte altre dovrebbero offrire funzionalità di delega degli eventi per appianare le differenze del browser e gestire i casi limite in modo pulito. jQuery fa certamente, con entrambe le sue livee delegatefunzioni, che consentono di specificare i gestori con una gamma completa di selettori CSS3 (e poi alcuni).

Ad esempio, ecco il codice equivalente che utilizza jQuery (tranne che sono sicuro che jQuery gestisce i casi limite, la versione grezza off-the-cuff sopra non lo fa):

(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);

Copia dal vivo


Non voglio usare Event.target, perché non è sempre l'obiettivo giusto. Giusto. Se fai clic su una IMGall'interno di a DIVe DIVha l'evento, Event.targetsarà IMGe non c'è modo di ottenere l' DIVoggetto bene. modifica Btw I hate .live'event' jQuery
Rudie

@Rudie: Re event.target: Quello che descrivi non è sbagliato, è corretto. Se fai clic su un imgall'interno di a dive stai guardando div, event.targetè il img(ed thisè il div). Dai un'altra occhiata a quanto sopra. Puoi collegare un gestore in qualsiasi punto della catena tra l'elemento su cui è stato fatto clic (il img, ad esempio) e l'elemento che stai guardando (in alto, fino in fondo document.body). Ri live: Preferisco di gran delegatelunga, la versione più contenuta di live. L'ho usato livesopra perché era il più vicino a quello che avevo fatto nell'esempio grezzo. La migliore,
TJ Crowder

4

Potresti voler creare una funzione di supporto come questa:

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

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

2
Perché questa è una cattiva soluzione? Perché le funzioni sono registrate nello spazio dei nomi globale? Chi ha votato per questo?
Rudie

Ottima idea
:)

4

Il tuo codice contiene un errore di battitura:

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

così

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

lavori. Saluti!

[ modifica ] in base al commento: e questo dovrebbe eseguire e restituire la funzione in una volta sola:

var addEventsAndStuff = (
 function(){
  var addeventsandstuff =  function(){
    alert('oele');
  };
  addeventsandstuff();
  return addeventsandstuff;
 }()
);

Le parentesi stupide sembrano tutte uguali !! Grazie! Salute davvero!
Rudie

Sì ... Quindi eh ... In realtà non funziona. La funzione viene eseguita immediatamente, sì, ma non è registrata da nessuna parte, quindi chiamandola di nuovo ->ReferenceError
Rudie

@Rudie: ho appena scoperto che KomodoEdit fa finalmente tutto ciò che ho sempre voluto che facesse Aptana Studio (ma si è rotto tutto il tempo), e per di più è altamente configurabile, veloce e ricco di utili componenti aggiuntivi. E mette in evidenza le parentesi corrispondenti: puoi persino dare a quelle parentesi graffe un colore di avvertimento rosso fermo! Provalo
KooiInc

Amico ... (E ovviamente l'ho digitato nell'editor web SO, non in un editor desktop.)
Rudie

E questo è un sacco di digitazione extra e ricorda cosa e come digitare. Mi è piaciuta la soluzione originale (non funzionante), ma quella nuova è troppo difficile =)
Rudie

2

Ancora più semplice con 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"

Quindi come lo chiamerei di nuovo? Qual è il suo nome, per chiamarlo? Stai solo mantenendo il risultato, non la funzione.
Rudie

Questo metodo viene utilizzato in alcuni casi solo se è necessario richiamarlo immediatamente e non si desidera eseguirlo nuovamente. Ad ogni modo modifico la risposta per mostrare come poterla richiamare di nuovo, cosa ne pensi? @Rudie
Alberto

1
La mia domanda riguardava il nome e la riesecuzione. concat_twofunziona perfettamente, MA lo definisce sempre nell'ambito globale, quindi non funzionerà con "use strict". Non è importante per me, ma è un piccolo svantaggio.
Rudie

1

Se vuoi creare una funzione ed eseguirla immediatamente -

// 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();

1
Attenzione, quando usi una funzione con nome come valore a destra (come fai sopra), stai usando un'espressione di funzione con nome che sebbene dovrebbe essere valida purtroppo ha problemi in varie implementazioni (principalmente - quelle shock - IE ). Dettagli: kangax.github.com/nfe
TJ Crowder

0

Prova a fare così:

var addEventsAndStuff = (function(){
    var func = function(){
        alert('ole!');
    };
    func();
    return func;
})();

1
Questo è vicino, ma var foo = (function() {...})();chiama la funzione prima dell'assegnazione. La parentesi deve includere l'assegnazione. (Vuoi assegnare quindi chiamare.)
David

È anche molto da scrivere E ricordare cosa digitare. Allora preferisco semplicemente usare le mie chiamate correnti.
Rudie
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.