Kann ich eine JavaScript-Funktion benennen und sofort ausführen?

88

Ich habe einige davon:

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

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

Das erneute Hinzufügen der Ereignisse ist erforderlich, da sich das DOM geändert hat und zuvor angehängte Ereignisse nicht mehr vorhanden sind. Da sie anfangs auch angebracht werden müssen (duh), haben sie eine gute Funktion, um trocken zu sein.

An diesem Setup ist nichts auszusetzen (oder gibt es das?), Aber kann ich es ein wenig glätten? Ich möchte die addEventsAndStuff()Funktion erstellen und sofort aufrufen, damit sie nicht so amateurhaft aussieht.

Beide folgenden antworten mit einem Syntaxfehler:

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

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

Irgendwelche Abnehmer?

Rudie
quelle
Mögliches Duplikat der selbstausführenden Javascript-Funktion
Cristian Sanchez
2
Nein, @CristianSanchez.
Máxima Alekz
Imho ist Ihr zweiter Codeblock schwer zu lesen. Im ersten Codeblock ist für jeden Leser klar, dass Sie die Funktion nach init aufrufen. Leser neigen dazu, schließende und überlesene Klammern am Ende zu ignorieren.
Kaiser

Antworten:

122

An dem Beispiel, das Sie in Ihrer Frage gepostet haben, ist nichts auszusetzen. Die andere Vorgehensweise mag seltsam aussehen, aber:

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

Es gibt zwei Möglichkeiten, eine Funktion in JavaScript zu definieren. Eine Funktionsdeklaration:

function foo(){ ... }

und eine Funktion zum Ausdruck, das ist eine beliebige Art und Weise eine andere Funktion als die oben definieren:

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

...etc

Funktionsausdrücke können benannt werden, aber ihr Name wird nicht an den enthaltenen Bereich weitergegeben. Das heißt, dieser Code ist gültig:

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

aber das ist nicht:

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

Um Ihre Funktion zu benennen und sofort aufzurufen, müssen Sie eine lokale Variable definieren, Ihre Funktion als Ausdruck zuweisen und dann aufrufen.

Mark Kahn
quelle
1
"Funktionsausdrücke können benannt werden" Vorsicht vor benannten Funktionsausdrücken. Sie sollten so funktionieren, wie Sie es beschreiben, aber nicht im IE vor IE9 - Sie erhalten zwei Funktionen, und der Funktionsname ist undicht. Details: kangax.github.com/nfe (Dies ist endlich in IE9 behoben.)
TJ Crowder
@TJ - danke für den Hinweis. Ich habe das nicht bemerkt, da ich den IE nur zum Testen von Endprodukten verwende :) Ich gehe davon aus, dass IE9 diesen Fehler nicht emuliert, wenn er in den IE7 / 8-Modus versetzt wird. Ich denke, es verwendet immer noch die IE9 JS-Engine ... Meh
Mark Kahn
Wenn Sie wie ich auf jshint-Probleme mit dieser Methode stoßen, können Sie die call()Methode von hier aus verwenden: stackoverflow.com/a/12359154/1231001
cfx
Dieser Ansatz mag unter bestimmten Umständen nützlich erscheinen, ist aber nicht nur etwas schwerer zu lesen, sondern scheint auch praktisch die gleiche Textmenge zu haben.
Vladimir
14

Hierfür gibt es eine gute Abkürzung (es müssen keine Variablen deklariert werden, außer für die Zuweisung der Funktion):

var func = (function f(a) { console.log(a); return f; })('Blammo')
James Gorrie
quelle
Welche rwird die Funktion zurückgeben? Das function roder das var r? Nur neugierig. Gute Lösung. Musste aber nicht ein Ort sein =)
Rudie
Ich habe es umbenannt, um es klarer zu machen, aber das return rwäre das gewesen, function rwie es der Umfang war. Ich habe Scala so permanent geschrieben, dass ich versucht habe, etwas online zu bringen.
James Gorrie
@ JamesGorrie, brillante Kurzschrift, ich habe es jedoch zweimal in der Konsole versucht, es scheint func, func (), func () () alle Rückgabefunktionsdeklaration von f (), aber können wir 'Blammo' von func () wie erwartet ausgeben :)?
Mike 652638
@ mike652638 Ich habe das in meiner Konsole ausgeführt und es Konsole protokolliert blammo?
James Gorrie
5

An diesem Setup ist nichts auszusetzen (oder gibt es das?), Aber kann ich es ein wenig glätten?

Verwenden Sie stattdessen die Ereignisdelegierung. Dort suchen Sie tatsächlich nach dem Ereignis in einem Container, der nicht verschwindet, und verwenden dann event.target(oder event.srcElementim Internet Explorer), um herauszufinden, wo das Ereignis tatsächlich aufgetreten ist, und es korrekt zu behandeln.

Auf diese Weise hängen Sie die Handler nur einmal an und sie funktionieren auch dann weiter, wenn Sie Inhalte austauschen.

Hier ist ein Beispiel für die Ereignisdelegierung ohne Verwendung von Hilfsbibliotheken:

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

})();

Live Beispiel

Beachten Sie, dass beim Klicken auf die Nachrichten, die dynamisch zur Seite hinzugefügt werden, Ihr Klick registriert und verarbeitet wird, obwohl kein Code zum Verknüpfen von Ereignissen mit den neu hinzugefügten Absätzen vorhanden ist. Beachten Sie auch, dass Ihre Handler nur Einträge in einer Karte sind und Sie einen Handler auf dem haben document.body, der den gesamten Versand erledigt. Nun, Sie wurzeln dies wahrscheinlich in etwas gezielterem als document.body, aber Sie bekommen die Idee. Im obigen Abschnitt versenden wir grundsätzlich nach id, aber Sie können das Matching so komplex oder einfach durchführen, wie Sie möchten.

Moderne JavaScript-Bibliotheken wie jQuery , Prototype , YUI , Closure oder eine von mehreren anderen sollten Funktionen zur Ereignisdelegierung bieten, um Browserunterschiede auszugleichen und Randfälle sauber zu behandeln. jQuery bietet sowohl seine liveals auch seine delegateFunktionen, mit denen Sie Handler mit einer ganzen Reihe von CSS3-Selektoren (und noch einige mehr) angeben können.

Hier ist zum Beispiel der entsprechende Code, der jQuery verwendet (außer ich bin sicher, dass jQuery Randfälle behandelt, die in der obigen Rohversion ohne Manschette nicht behandelt werden):

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

Live-Kopie

TJ Crowder
quelle
Ich möchte nicht verwenden Event.target, weil das nicht immer das richtige Ziel ist. Das stimmt. Wenn Sie auf ein IMGinnerhalb eines klicken DIVund das DIVEreignis hat, Event.targetwird das sein IMGund es gibt keine Möglichkeit, das DIVObjekt schön zu bekommen . bearbeiten Btw ich hasse jQuery .live‚Ereignis‘
Rudie
@ Rudie: Re event.target: Was du beschreibst, ist nicht falsch, es ist richtig. Wenn Sie auf ein imginnerhalb von a klicken divund das beobachten div, event.targetist das img(und thisist das div). Schauen Sie sich das oben noch einmal an. Sie können an jedem Punkt in der Kette einen Handler zwischen dem angeklickten Element ( imgz. B. dem) und dem Element, das Sie beobachten (oben ganz unten document.body), anhängen . Re live: Ich bevorzuge sehr delegatedie geschlossenere Version von live. Ich habe es liveoben verwendet, weil es dem am nächsten kam, was ich im Rohbeispiel getan hatte. Beste,
TJ Crowder
4

Möglicherweise möchten Sie eine Hilfsfunktion wie folgt erstellen:

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

defineAndRun('addEventsAndStuff', function() {
    alert('oele');
});
pimvdb
quelle
2
Warum ist das eine schlechte Lösung? Weil die Funktionen im globalen Namespace registriert sind? Wer hat dafür gestimmt?
Rudie
Tolle Idee
:)
4

Ihr Code enthält einen Tippfehler:

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

so

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

funktioniert. Prost!

[ bearbeiten ] basierend auf Kommentar: und dies sollte ausgeführt werden und die Funktion auf einmal zurückgeben:

var addEventsAndStuff = (
 function(){
  var addeventsandstuff =  function(){
    alert('oele');
  };
  addeventsandstuff();
  return addeventsandstuff;
 }()
);
KooiInc
quelle
Dumme Klammern sehen alle gleich aus !! Vielen Dank! Prost in der Tat!
Rudie
Ja ... Also eh ... Eigentlich funktioniert das nicht. Die Funktion wird sofort ausgeführt, ja, aber sie ist nirgendwo registriert, also rufen Sie sie erneut auf ->ReferenceError
Rudie
@Rudie: Ich habe gerade entdeckt, dass KomodoEdit endlich alles macht, was ich immer von Aptana Studio wollte (aber immer kaputt ging), und obendrein ist es hochgradig konfigurierbar, schnell und voller nützlicher Addons. Und es hebt passende Klammern hervor: Sie können diesen passenden Klammern sogar eine feste rote Warnfarbe geben! Probieren
KooiInc
Alter ... (Und natürlich habe ich es im SO-Web-Editor eingegeben, nicht in einem Desktop-Editor.)
Rudie
Und das ist viel zusätzliches Tippen und Erinnern, was und wie man tippt. Ich mochte die ursprüngliche (nicht funktionierende) Lösung, aber die neue ist zu schwierig =)
Rudie
2

Mit ES6 noch einfacher:

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
quelle
Wie würde ich es wieder nennen? Wie heißt es, um es zu nennen? Sie behalten nur das Ergebnis, nicht die Funktion.
Rudie
Diese Methode wird in einigen Fällen nur verwendet, wenn Sie sie sofort aufrufen müssen und sie nicht erneut ausführen möchten. Wie auch immer, ich werde die Antwort bearbeiten, um zu zeigen, wie man sie wieder aufrufen kann. Was denkst du? @ Rudie
Alberto
1
Meine Frage war, es zu benennen und erneut auszuführen. concat_twofunktioniert perfekt, ABER es definiert es immer im globalen Bereich, so dass es nicht funktioniert "use strict". Das ist für mich nicht wichtig, aber es ist ein kleiner Nachteil.
Rudie
1

Wenn Sie eine Funktion erstellen und sofort ausführen möchten -

// 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
quelle
1
Beachten Sie, dass Sie, wenn Sie eine benannte Funktion als rechten Wert verwenden (wie oben beschrieben), einen benannten Funktionsausdruck verwenden, der zwar gültig sein sollte , aber leider Probleme in verschiedenen Implementierungen aufweist (meistens - Quelle Shock - IE) ). Details: kangax.github.com/nfe
TJ Crowder
0

Versuchen Sie es so:

var addEventsAndStuff = (function(){
    var func = function(){
        alert('ole!');
    };
    func();
    return func;
})();
Alexander Ruliov
quelle
1
Dies ist nah, var foo = (function() {...})();ruft aber die Funktion vor der Zuweisung auf. Die Klammer muss die Zuordnung enthalten. (Sie möchten zuweisen, dann anrufen.)
David
Das ist auch viel Tippen UND sich daran erinnern, was man tippen soll. Dann nutze ich lieber meine aktuellen Anrufe.
Rudie