web-dev-qa-db-fra.com

Que signifient les parenthèses entourant une déclaration d'objet / fonction / classe?

Je suis nouveau sur JavaScript et YUI . Dans les exemples de bibliothèques YUI, vous pouvez trouver de nombreuses utilisations de cette construction:

(function() {
    var Dom = YAHOO.util.Dom,
    Event = YAHOO.util.Event,
    layout = null,
        ...
})();

Je pense que les deux dernières parenthèses sont à exécuter la fonction juste après la déclaration.

... Mais qu'en est-il des parenthèses précédentes entourant la déclaration de fonction?

Je pense que c'est une question de portée. c'est pour cacher des variables internes à des fonctions extérieures et éventuellement à des objets globaux. Est-ce Plus généralement, quels sont les mécanismes de ces parenthèses?

284
user54692

C'est une fonction anonyme à exécution automatique. Le premier ensemble de parenthèses contient les expressions à exécuter et le deuxième ensemble de parenthèses exécute ces expressions.

C'est une construction utile lorsque vous essayez de masquer des variables de l'espace de noms parent. Tout le code de la fonction est contenu dans la portée privée de la fonction, ce qui signifie qu'il est impossible d'y accéder du tout en dehors de la fonction, ce qui le rend vraiment privé.

Voir:

http://en.wikipedia.org/wiki/Closure_%28computer_science%29

http://peter.michaux.ca/articles/javascript-namespacing

213
Andy Hume

Andy Hume à peu près a donné la réponse, je veux juste ajouter quelques détails supplémentaires.

Avec cette construction, vous créez une fonction anonyme avec son propre environnement d'évaluation ou de fermeture, puis vous l'évaluez immédiatement. La bonne chose à ce sujet est que vous pouvez accéder aux variables déclarées avant la fonction anonyme, et que vous pouvez utiliser des variables locales dans cette fonction sans écraser par inadvertance une variable.

L'utilisation du mot clé var est très importante car, dans JavaScript, chaque variable est globale par défaut, mais vous créez un nouveau mot clé , lexical scoped. variable, c’est-à-dire qu’elle est visible par le code situé entre les deux accolades . Dans votre exemple, vous créez essentiellement des alias courts pour les objets de la bibliothèque YUI, mais ses utilisations sont plus puissantes.

Je ne veux pas vous laisser sans exemple de code, je vais donc vous donner un exemple simple pour illustrer une fermeture:

var add_gen = function(n) {
  return function(x) {
    return n + x;
  };
};
var add2 = add_gen(2);
add2(3); // result is 5

Qu'est-ce qui se passe ici? Dans la fonction add_gen , vous créez une autre fonction qui va simplement ajouter le nombre n à son argument. L'astuce est que, dans les variables définies dans la liste des paramètres de la fonction, agissent comme des variables à portée lexicale, comme celles définies avec var .

La fonction retournée est définie entre les accolades de la fonction add_gen afin qu'elle ait accès à la valeur of n même après l'exécution de la fonction add_gen, c'est pourquoi vous obtiendrez 5 lors de l'exécution de la dernière ligne de l'exemple.

À l'aide de paramètres de fonction à portée lexicale, vous pouvez contourner les "problèmes" résultant de l'utilisation de variables de boucle dans des fonctions anonymes. Prenons un exemple simple:

for(var i=0; i<5; i++) {
  setTimeout(function(){alert(i)}, 10);
}

Le résultat "attendu" pourrait être les nombres de zéro à quatre, mais vous obtenez quatre instances de fives. Cela se produit parce que la fonction anonyme dans setTimeout et la boucle for utilisent la variable très identique i , donc au moment où les fonctions sont évaluées, i sera 5.

Vous pouvez obtenir le résultat attendu naïvement en utilisant la technique de votre question et le fait que les paramètres de la fonction sont étendus lexicalement. (J'ai utilisé cette approche dans un autre réponse )

for(var i=0; i<5; i++) {
  setTimeout(
     (function(j) {
       return function(){alert(j)};
     })(i), 10);
}

Avec l'évaluation immédiate de la fonction externe, vous créez une variable complètement indépendante nommée j à chaque itération, et la valeur actuelle de i sera copié dans cette variable, vous obtiendrez ainsi le résultat attendu naïvement du premier essai.

Je vous suggère d'essayer de comprendre l'excellent tutoriel sur http://ejohn.org/apps/learn/ afin de mieux comprendre les fermetures, c'est là que j'ai beaucoup appris.

140
bandi

... mais qu'en est-il des parentes précédentes du tour entourant toute la déclaration de fonction?

En particulier, cela fait que JavaScript interprète la construction 'function () {...}' comme une expression de fonction anonyme en ligne. Si vous avez omis les crochets:

function() {
    alert('hello');
}();

Vous obtiendrez une erreur de syntaxe, car l'analyseur JS verrait le mot clé 'function' et supposerait que vous démarrez une fonction statement de la forme:

function doSomething() {
}

... et vous ne pouvez pas avoir une instruction de fonction sans un nom de fonction.

les expressions de fonction et les instructions de fonction sont deux structures différentes qui sont gérées de manière très différente. Malheureusement, la syntaxe est presque identique, ce qui ne confond pas le programmeur, même l'analyseur a du mal à dire ce que vous voulez dire!

46
bobince

Juts pour donner suite à ce qu'Andy Hume et d'autres ont dit:

Le "()" entourant la fonction anonyme est l'opérateur de groupement tel que défini dans la section 11.1.6 de la spécification ECMA: http://www.ecma-international.org/publications/files/ECMA-ST /Ecma-262.pdf .

Extrait textuellement de la documentation:

11.1.6 L'opérateur de groupement

La production Expression primaire : ( Expression ) est évalué comme suit:

  1. Renvoie le résultat de l'évaluation Expression. Cela peut être de type référence.

Dans ce contexte, la fonction est traitée comme une expression.

14
user484261

Quelques considérations sur le sujet:

  • La parenthèse:

    Le navigateur (moteur/analyseur) associe la fonction de mot-clé à

    [optional name]([optional parameters]){...code...}
    

    Ainsi, dans une expression comme function () {} (), la dernière parenthèse n’a aucun sens.

    Maintenant pense à

    name=function(){} ; name() !?
    

Oui, la première paire de parenthèses force la fonction anonyme à se transformer en une variable (expression stockée) et la seconde lance l'évaluation/l'exécution, donc ( function () {} ) () est logique.

  • L'utilité: ?

    1. Pour exécuter du code lors du chargement et isoler les variables utilisées du reste de la page, en particulier lorsque des conflits de noms sont possibles;

    2. Remplacez eval ("chaîne") par

      (nouvelle fonction ("chaîne")) ()

    3. Enveloppe le long code pour l'opérateur "= ?:" comme:

      résultat = exp_to_test? (function () {... code long ...}) (): (fonction () {...}) ();

6
bortunac

Les premières parenthèses sont pour, si vous voulez, l'ordre des opérations. Le 'résultat' de l'ensemble des parenthèses entourant la définition de la fonction est la fonction elle-même que le deuxième ensemble de parenthèses exécute en effet.

Quant à savoir pourquoi c'est utile, je ne suis pas assez d'un assistant JavaScript pour en avoir une idée. : P

4
Sean Edwards

Voir cette question . Le premier ensemble de parenthèses n'est pas nécessaire si vous utilisez un nom de fonction, mais une fonction sans nom requiert cette construction et la parenthèse sert à permettre aux codeurs de se rendre compte qu'ils ont affiché une fonction invocante lors de la navigation dans le code (voir celui d'un blogueur - recommandation de meilleures pratiques ).

1
palswim