fonction javascript menant bang! syntaxe

204

J'ai vu cette syntaxe sur quelques bibliothèques maintenant et je me demande quel est l'avantage. (notez que je suis bien au courant des fermetures et de ce que fait le code, je ne me préoccupe que des différences syntaxiques)

!function(){
  // do stuff
}();

Comme alternative aux plus courants

(function(){
  // do stuff
})();

pour l'auto-invocation de fonctions anonymes.

Je me pose quelques questions. Tout d'abord, qu'est-ce qui permet au meilleur exemple de fonctionner? Pourquoi le coup est-il nécessaire pour rendre cette déclaration syntaxiquement correcte? On me dit aussi que ça +marche, et je suis sûr que certains autres, à la place de!

Deuxièmement, quel est l'avantage? Tout ce que je peux dire, c'est qu'il enregistre un seul personnage, mais je ne peux pas imaginer que c'est un énorme avantage pour attirer de nombreux adoptants. Y a-t-il un autre avantage qui me manque?

La seule autre différence que je peux voir serait la valeur de retour de la fonction auto-invoquante, mais dans ces deux exemples, nous ne nous soucions pas vraiment de la valeur de retour de la fonction car elle n'est utilisée que pour créer une fermeture. Alors, quelqu'un peut-il me dire pourquoi on pourrait utiliser la première syntaxe?

Brad
la source
Les cadres aiment enregistrer autant de caractères que possible qu'un minificateur ne peut pas optimiser.
alex
Le premier avantage me vient à l'esprit, c'est que vous pouvez créer un sandbox comme, (fonction ($) {...}) (jQuery);
JS Taylor
2
les deux exemples y parviennent. Comme mentionné, je comprends ce que font ces fonctions, je suis plus intéressé par les différences syntaxiques
brad
8
Je l'attribue au besoin, nee, d'exigence d'être mignon d'une manière plus sophistiquée que le précédent équipage. "Oh, ces païens entre parenthèses, nous avons !!"
Jared Farrish
2
Je n'ai jamais su ça, génial. J'aime le !car il souligne qu'il est en cours d'exécution.
cdmckay

Réponses:

97

Idéalement, vous devriez pouvoir faire tout cela simplement:

function(){
  // do stuff
}(); 

Cela signifie déclarer une fonction anonyme et l'exécuter. Mais cela ne fonctionnera pas en raison des spécificités de la grammaire JS.

La forme la plus courte pour y parvenir est d'utiliser une expression, par exemple UnaryExpression (et donc CallExpression):

!function(){
  // do stuff
}(); 

Ou pour le plaisir:

-function(){
  // do stuff
}(); 

Ou:

+function(){
  // do stuff
}(); 

Ou même:

~function(){
  // do stuff
  return 0;
}( );
c-sourire
la source
3
alors le seul avantage de la concision?
brad
12
J'ai ajouté toutes les options ci-dessus au test de performance sur: jsperf.com/bang-function
Shaz
5
Expressivité, je dirais. La vitesse n'a pas vraiment d'importance dans de tels cas car elle ne fonctionne qu'une seule fois.
c-smile
1
Par curiosité, j'ai remarqué qu'aucun bang et bang ne sont les plus rapides, mais quelque part dans l'évolution de Chrome au moins, le bang est devenu plus rapide que le bang. (Probablement statistiquement insignifiant mais ...) Y a-t-il une raison pour laquelle? Je ne connais pas grand-chose au code sous le capot, mais je le trouve assez fascinant.
jmk2142
Deux de vos opérateurs unaires peuvent être interprétés comme binaires si un point-virgule est manquant. Le premier et le dernier sont les plus sûrs de ces exemples.
73

En Javascript, une ligne commençant par functionest censée être une déclaration de fonction et est censée ressembler à

function doSomething() {
}

Une fonction auto-invoquante comme

function(){
  // do stuff
}();

ne correspond pas à cette forme (et provoquera une erreur de syntaxe au premier paren d'ouverture car il n'y a pas de nom de fonction), donc les crochets sont utilisés pour délimiter une expression de fonction anonyme .

(function(){
  // do stuff
})();

Mais tout ce qui crée une expression (par opposition à une déclaration de fonction) fera l'affaire, d'où le !. C'est dire à l'interprète que ce n'est pas une déclaration de fonction. En dehors de cela, la priorité de l'opérateur impose que la fonction soit invoquée avant la négation.

Je n'étais pas au courant de cette convention, mais si elle devient courante, elle peut contribuer à la lisibilité. Ce que je veux dire, c'est que quiconque lisant le !functionhaut d'un grand bloc de code s'attendra à une auto-invocation, la façon dont nous sommes déjà conditionnés à attendre la même chose quand nous voyons (function. Sauf que nous perdrons ces parenthèses ennuyeuses. Je m'attends à ce que ce soit la raison, par opposition à toute économie de vitesse ou de nombre de caractères.

brainjam
la source
Cette "ligne commençant par la fonction est attendue ..." semble assez floue. Qu'en est-il de cela:var foo = {CR/LF here} function bar() {}
c-smile
45

Outre les choses qui ont déjà été dites, la syntaxe avec le! est utile si vous écrivez javascript sans point-virgule:

var i = 1
!function(){
  console.log('ham')
}()

i = 2
(function(){
  console.log('cheese')
})()

Le premier exemple renvoie «ham» comme prévu, mais le second générera une erreur car l'instruction i = 2 n'est pas terminée en raison de la parenthèse suivante.

De plus, dans les fichiers javascript concaténés, vous n'avez pas à vous inquiéter si le code précédent manque de points-virgules. Donc pas besoin du commun; (function () {}) (); pour vous assurer que le vôtre ne se cassera pas.

Je sais que ma réponse est un peu tard mais je pense qu'elle n'a pas encore été mentionnée :)

Smoe
la source
6
+1 pour le conseil et les souvenirs ... personne n'aime écrire du javascript sans point-virgule de nos jours.
Pablo Grisafi
4
Twitter Bootstrap est tout JS sans point-virgule, et c'est assez nouveau ... (désolé si le commentaire ci-dessus était de nature sarcastique et que je l'ai manqué).
brandwaffle
2
Ce n'est pas vraiment vrai. Prenons l'exemple suivant:! Function () {console.log ("ham");} ()! Function () {console.log ("cheese")} (); vous obtenez une erreur: "SyntaxError: jeton inattendu!"
Heavysixer
Oui, tu as raison. Il y a une erreur, si vous les mettez sur la même ligne. Je n'y ai pas pensé. Mais, aucun script / lib de concaténation que j'ai utilisé jusqu'à présent ne le fait. Et lorsque vous réduisez et concatendez vos fichiers, des points-virgules sont ajoutés. Donc, honnêtement, je ne peux pas imaginer un cas où vous rencontriez ce problème. D'un autre côté, il est 2h du matin ici et je suis peut-être ignorant :)
Smoe
6

D'une part, jsPerf montre que l'utilisation de !(UnaryExpression) est généralement plus rapide. Parfois, ils se révèlent égaux, mais quand ils ne le sont pas, je n'ai pas encore vu le non-frappé triompher trop des autres: http://jsperf.com/bang-function

Cela a été testé sur le dernier Ubuntu avec le plus ancien (disons ..) Chrome, version 8. Les résultats peuvent donc différer bien sûr.

Edit: Que diriez-vous de quelque chose de fou comme delete?

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

ou void?

void function() {
   alert("Hi!"); 
}();
Shaz
la source
intéressant! J'ai obtenu environ 50/50 bien que dans Safari en termes de vitesse, les non-cognés se soient certainement maintenus. Je me demande s'il y a une théorie sur les différences de vitesse potentielles?
brad
@brad Doit être quelque chose à voir avec safari, si vous regardez les tests, la plupart des autres navigateurs semblent être en faveur !. Mais j'aimerais aussi trouver une théorie à ce sujet :)
Shaz
3
j'aime void parce qu'il dit explicitement "je ne me soucie pas de la valeur de retour", et parce qu'il me rappelle les conventions dans d'autres langues
code_monk
4

Comme vous pouvez le voir ici , la meilleure façon de faire des méthodes auto-invoquées en javascript est d'utiliser:

(function(){}()); -> 76,827,475 ops/sec

!function(){}();  -> 69,699,155 ops/sec

(function(){})(); -> 69,564,370 ops/sec
Geku
la source
1
Je doute que les performances soient la raison d'utiliser une syntaxe ou l'autre. À 70 millions d'opérations / seconde, déclarer la fermeture sera de toute façon des milliers de fois plus rapide que le code à l'intérieur
Eloims
3

Donc, avec niez "!" et tous les autres opérateurs unaires comme +, -, ~, delete, void, beaucoup a été dit, juste pour résumer:

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

Ou

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

Ou

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

Et quelques autres cas avec des opérateurs binaires pour le plaisir :)

1 > function() {
   alert("Hi!"); 
}();

Ou

1 * function() {
   alert("Hi!"); 
}();

Ou

1 >>> function() {
   alert("Hi!"); 
}();

Ou même

1 == function() {
   alert("Hi!"); 
}();

Quitter le ternaire pour quelqu'un d'autre :)

Arman McHitarian
la source
12
0?0:function() { alert("Hi!"); }();
daniel1426
Bingo, Dan! Nous avons le ternaire;)
Arman McHitarian