Emplacement des parenthèses pour l'exécution automatique des fonctions JavaScript anonymes?

107

J'ai récemment comparé la version actuelle de json2.js avec la version que j'avais dans mon projet et j'ai remarqué une différence dans la façon dont l'expression de fonction était créée et auto-exécutée.

Le code utilisé pour mettre une fonction anonyme entre parenthèses puis l'exécuter,

(function () {
  // code here
})();

mais maintenant, il enveloppe la fonction exécutée automatiquement entre parenthèses.

(function () {
  // code here
}());

Il y a un commentaire du CMS dans la réponse acceptée de la syntaxe de la fonction anonyme encapsulée de JavaScript expliquant que «les deux: (function(){})();et (function(){}());sont valides».

Je me demandais quelle est la différence? Le premier prend-il mémoire en laissant autour d'une fonction globale et anonyme? Où doit être située la parenthèse?

Kevin Hakanson
la source
En relation: Syntaxe d'invocation de fonction immédiate (dans JSLint)
Bergi
1
Lisez également le but de cette construction , ou consultez une explication ( technique ) (également ici ). Pour savoir pourquoi les parenthèses sont nécessaires, consultez cette question .
Bergi

Réponses:

66

Ils sont pratiquement les mêmes.

La première enveloppe les parenthèses autour d'une fonction pour en faire une expression valide et l'appelle. Le résultat de l'expression n'est pas défini.

Le second exécute la fonction et les parenthèses autour de l'appel automatique en font une expression valide. Il évalue également comme indéfini.

Je ne pense pas qu'il y ait une «bonne» façon de le faire, puisque le résultat de l'expression est le même.

> function(){}()
SyntaxError: Unexpected token (
> (function(){})()
undefined
> (function(){return 'foo'})()
"foo"
> (function(){ return 'foo'}())
"foo"
meder omuraliev
la source
8
JSLint veut "(function () {} ());". JSLint dit: "Déplacez l'invocation dans les parenthèses contenant la fonction."
XP1
27
En fait, vous n'êtes pas limité à ces deux-là, vous pouvez utiliser à peu près tout ce qui permet au compilateur de réaliser que la fonction fait partie d'une expression et non d'une instruction, telle que +function(){}()ou !function(){}().
Tgr
49
@ XP1: JSLint veut beaucoup de choses qui sont spécifiques au style de Crockford plutôt que d'être substantielles. C'est l'un d'eux.
TJ Crowder
@TJCrowder. Que recommanderais-tu? jQuery utilise le premier style et Crockford utilise le second.
Teej
4
@ThorpeObazee: Cela n'a vraiment pas d'importance, alors faites ce que vous préférez. Je recommanderais contre certains des opérateurs les plus étendus ( -function(){}();, !function(){}();et essentiellement tout autre opérateur juste avant de functiontravailler, mais je m'en tiendrai aux versions utilisant des parens). Je vois le premier beaucoup plus que je ne vois le second, et c'est ma préférence; cela a plus de sens pour moi aussi, mais c'est subjectif. FWIW: jsbin.com/ejaqow
TJ Crowder
13

Dans ce cas, cela n'a pas d'importance. Vous appelez une expression qui se résout en une fonction dans la première définition, et définissez et appelez immédiatement une fonction dans le deuxième exemple. Ils sont similaires car l'expression de fonction dans le premier exemple n'est que la définition de fonction.

Il existe d'autres cas plus manifestement utiles pour appeler des expressions qui se résolvent en fonctions:

(foo || bar)()
Triptyque
la source
3
Pour clarifier les autres lecteurs (principalement parce que je ne l'ai pas compris moi-même au début :)), foo et / ou bar doivent déjà avoir une fonction. (par exemple foo = function(){alert('hi');}. Si aucune des fonctions n'est une fonction, une erreur est générée.
Alexander Bird
3
@AlexanderBird Une clarification supplémentaire - Il lancera également une erreur si fooc'est "véridique" mais pas une fonction.
JLRishe
9

Il n'y a aucune différence au-delà de la syntaxe.

Concernant vos préoccupations concernant la deuxième méthode pour le faire:

Considérer:

(function namedfunc () { ... }())

namedfuncne sera toujours pas dans la portée globale même si vous avez fourni le nom. Il en va de même pour les fonctions anonymes. La seule façon de l'obtenir dans cette portée serait de l'assigner à une variable à l'intérieur des parens.

((namedfunc = function namedfunc () { ... })())

Les parens externes ne sont pas nécessaires:

(namedfunc = function namedfunc () { ... })()

Mais vous ne vouliez pas de cette déclaration mondiale de toute façon, n'est-ce pas?

Cela se résume donc à:

(function namedfunc () { ... })()

Et vous pouvez le réduire encore plus: le nom est inutile car il ne sera jamais utilisé (à moins que votre fonction ne soit récursive .. et même alors vous pourriez l'utiliser arguments.callee)

(function () { ... })()

C'est ainsi que j'y pense (peut-être incorrect, je n'ai pas encore lu la spécification ECMAScript). J'espère que ça aide.

Cristian Sanchez
la source
Notez que arguments.calleec'est obsolète depuis ES5 (et interdit en mode strict).
nyuszika7h
"Les parenthèses externes ne sont pas nécessaires:" - Je pense qu'elles empêchent les erreurs lorsque les fichiers sont concaténés, sinon vous auriez besoin d'un! ou quelque chose.
Jimmyt1988
-3

La différence existe simplement parce que Douglas Crockford n'aime pas le premier style pour les IIFE ! (seriuosly) Comme vous pouvez le voir dans cette vidéo !! .

La seule raison de l'existence du wrapping supplémentaire (){dans les deux styles} est d'aider à créer cette section du code Function Expression , car la déclaration de fonction ne peut pas être immédiatement appelée. Certains scripts / rapetisser-ers utilisation juste +, !, -et au ~lieu de trop entre parenthèses. Comme ça:

+function() {  
    var foo = 'bar';  
}();

!function() {  
    var foo = 'bar';  
}();

-function() {  
    var foo = 'bar';  
}();

~function() {  
    var foo = 'bar';  
}();

Et tout cela est exactement le même que vos alternatives. Le choix parmi ces cas est entièrement indépendant et ne fait aucune différence. {Ceux qui ()produisent un fichier plus grand de 1 octet ;-)}

behradkhodayar
la source