108 votes

Puis-je nommer une fonction JavaScript et l'exécuter immédiatement ?

J'en ai plusieurs :

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

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

Il est nécessaire d'ajouter à nouveau les événements parce que le DOM a changé et que les événements précédemment attachés ont disparu. Puisqu'ils doivent également être attachés initialement (duh), ils sont dans une belle fonction pour être DRY.

Il n'y a rien de mal à cette configuration (ou n'est-ce pas ?), mais puis-je l'adoucir un peu ? J'aimerais créer le addEventsAndStuff() et l'appeler immédiatement, afin de ne pas donner l'impression d'être un amateur.

Les deux suivants répondent par une erreur de syntaxe :

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

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

Y a-t-il des preneurs ?

148voto

zyklus Points 31683

Il n'y a rien de mal à l'exemple que vous avez posté dans votre question L'autre façon de procéder peut sembler étrange, mais.. :

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

Il existe deux façons de définir une fonction en JavaScript. Une déclaration de fonction :

function foo(){ ... }

et une expression de fonction, qui est tous de définir une fonction autre que celle décrite ci-dessus :

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

...etc

Les expressions de fonctions peuvent être nommées, mais leur nom n'est pas propagé dans l'espace contenant. Ce code est donc valide :

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

mais ce n'est pas le cas :

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

Ainsi, pour nommer votre fonction et l'appeler immédiatement, vous devez définir une variable locale, y affecter votre fonction sous forme d'expression, puis l'appeler.

21voto

James Gorrie Points 23

Il existe un bon raccourci pour cela (il n'est pas nécessaire de déclarer des variables à la fin de l'affectation de la fonction) :

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

7voto

T.J. Crowder Points 285826

Il n'y a rien de mal à cette configuration (ou n'y en a-t-il pas ?), mais puis-je l'adoucir un peu ?

Envisagez plutôt de recourir à la délégation d'événements. Il s'agit de guetter l'événement sur un conteneur qui ne disparaître, puis utiliser event.target (ou event.srcElement sur IE) pour déterminer où l'événement s'est réellement produit et le traiter correctement.

De cette façon, vous n'attachez le(s) gestionnaire(s) qu'une seule fois et il(s) continue(nt) à fonctionner même si vous changez de contenu.

Voici un exemple de délégation d'événements sans utiliser de librairie d'aide :

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

})();

Exemple concret

Notez que si vous cliquez sur les messages qui sont ajoutés dynamiquement à la page, votre clic est enregistré et traité même s'il n'y a pas de code pour accrocher des événements sur les nouveaux paragraphes qui sont ajoutés. Notez également que vos gestionnaires ne sont que des entrées dans une carte, et que vous avez un sur le gestionnaire document.body qui s'occupe de tout le dispatching. Maintenant, vous devez probablement Rooter ceci dans quelque chose de plus ciblé que document.body mais vous voyez l'idée. De plus, dans ce qui précède, nous envoyons essentiellement par id mais vous pouvez faire des correspondances aussi complexes ou simples que vous le souhaitez.

Les bibliothèques JavaScript modernes telles que jQuery , Prototype , YUI , Fermeture o un autre parmi d'autres devrait offrir des fonctions de délégation d'événements afin d'aplanir les différences entre les navigateurs et de gérer proprement les cas particuliers. jQuery le fait assurément, avec ses deux modules live y delegate qui vous permettent de spécifier des gestionnaires utilisant une gamme complète de sélecteurs CSS3 (et plus encore).

Par exemple, voici le code équivalent en utilisant jQuery (sauf que je suis sûr que jQuery gère les cas limites, ce qui n'est pas le cas de la version brute ci-dessus) :

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

Copie en direct

7voto

KooiInc Points 38845

Votre code contient une faute de frappe :

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

donc

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

œuvre. A la vôtre !

[ éditer ] basé sur le commentaire : et ceci devrait exécuter et retourner la fonction en une seule fois :

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

4voto

pimvdb Points 66332

Vous pouvez créer une fonction d'aide comme celle-ci :

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X