Var functionName = function () {} vs function functionName () {}

Récemment, j'ai commencé à supporter le code JavaScript d'un autre utilisateur. Je corrige les erreurs, j'ajoute des fonctions et j'essaie également de ranger le code et de le rendre plus cohérent.

Le développeur précédent utilise deux méthodes pour déclarer des fonctions, et je ne peux pas déterminer s’il ya une raison à cela ou non.

Deux manières sont:

 var functionOne = function() { // Some code }; 
 function functionTwo() { // Some code } 

Quelles sont les raisons d'utiliser ces deux méthodes différentes et quels sont les avantages et les inconvénients de chacune d'elles? Y a-t-il quelque chose qui peut être fait avec une méthode qui ne peut pas être fait avec une autre?

6296
03 дек. Richard Garside a demandé 03 décembre 2008-12-03 14:31 '08 à 14h31 2008-12-03 14:31
@ 37 réponses
  • 1
  • 2

La différence est que functionOne est une expression d'une fonction et que, par conséquent, elle n'est déterminée que lorsque cette ligne est atteinte, alors que functionTwo est une déclaration de fonction et est déterminée dès que la fonction ou le script qui l'entoure est exécuté (en raison de la levée ).

Par exemple, la fonction expression:

 // Outputs: "Hello!" functionTwo(); function functionTwo() { console.log("Hello!"); } 

Cela signifie également que vous ne pouvez pas définir de manière conditionnelle des fonctions à l'aide de déclarations de fonctions:

 if (test) { // Error or misbehavior function functionThree() { doSomething(); } } 

Ce qui précède définit en réalité functionThree quelle que soit la valeur de test - sauf si use strict action use strict , auquel cas elle provoque simplement une erreur.

4669
03 дек. réponse donnée par Greg 03 déc. 2008-12-03 14:37 2008 à 14h37 2008-12-03 14:37

Je veux d’abord réparer Greg: la function abc(){} également limitée; le nom abc est défini dans la zone où se trouve cette définition. Exemple:

 function xyz(){ function abc(){}; // abc is defined here... } // ...but not here 

Deuxièmement, vous pouvez combiner les deux styles:

 var xyz = function abc(){}; 

xyz sera défini comme d'habitude, abc - non défini dans tous les navigateurs, mais Internet Explorer - ne repose pas sur sa définition. Mais il sera défini dans son corps:

 var xyz = function abc(){ // xyz is visible here // abc is visible here } // xyz is visible here // abc is undefined here 

Si vous souhaitez utiliser des pseudonymes dans tous les navigateurs, utilisez ce type d'annonce:

 function abc(){}; var xyz = abc; 

Dans ce cas, xyz et abc sont des alias du même objet:

 console.log(xyz === abc); // prints "true" 

L'une des raisons impérieuses pour utiliser le style combiné est l'attribut "name" des objets de fonction ( non pris en charge par Internet Explorer ). Fondamentalement, lorsque vous définissez une fonction comme

 function abc(){}; console.log(abc.name); // prints "abc" 

son nom est automatiquement attribué. Mais quand vous le définissez comme

 var abc = function(){}; console.log(abc.name); // prints "" 

son nom est vide - nous avons créé une fonction anonyme et lui avons attribué une variable.

Une autre bonne raison d'utiliser le style combiné est d'utiliser un nom interne court pour s'y référer, fournissant ainsi un nom long et non conflictuel aux utilisateurs externes:

 // Assume really.long.external.scoped is {} really.long.external.scoped.name = function shortcut(n){ // Let it call itself recursively: shortcut(n - 1); // ... // Let it pass itself as a callback: someFunction(shortcut); // ... } 

Dans l'exemple ci-dessus, nous pouvons faire la même chose avec le nom externe, mais ce sera trop lourd (et plus lent).

(Une autre façon de vous adresser à vous-même consiste à utiliser arguments.callee , qui est encore relativement long et qui n'est pas pris en charge en mode strict.)

Down, JavaScript gère les deux instructions différemment. C'est une déclaration de fonction:

border=0
 function abc(){} 

abc est défini partout dans la zone actuelle:

 // We can call it here abc(); // Works // Yet, it is defined down there. function abc(){} // We can call it again abc(); // Works 

En outre, il a augmenté en utilisant la return :

 // We can call it here abc(); // Works return; function abc(){} 

Ceci est une expression de fonction:

 var xyz = function(){}; 

xyz est défini ici à partir de la destination:

 // We can't call it here xyz(); // UNDEFINED!!! // Now it is defined xyz = function(){} // We can call it here xyz(); // works 

La déclaration de la fonction et l'expression de la fonction sont la véritable raison pour laquelle il y a une différence, démontrée par Greg.

Fait amusant:

 var xyz = function abc(){}; console.log(xyz.name); // Prints "abc" 

Personnellement, je préfère la déclaration "expression de fonction", car je peux ainsi contrôler la visibilité. Quand je définis une fonction type

 var abc = function(){}; 

Je sais que j'ai défini la fonction localement. Quand je définis une fonction type

 abc = function(){}; 

Je sais que je l'ai défini globalement, indiquant que je n'ai défini abc nulle part dans la chaîne de régions. Ce style de définition est stable même lorsqu'il est utilisé dans eval() . Bien que la définition

 function abc(){}; 

dépend du contexte et peut vous laisser vous demander où il est défini, en particulier dans le cas de eval() - Réponse: Cela dépend du navigateur.

1846
03 дек. La réponse est donnée par Eugene Lazutkin 03 déc. 2008-12-03 20:43 '08 à 20h43 2008-12-03 20:43

Voici un résumé des formulaires standard qui créent des fonctions: (Initialement écrit pour une autre question, mais adapté après le passage à la question canonique.)

Termes

Liste rapide:

  • Déclaration de fonction

  • function "anonyme" Expression (qui, malgré le terme, crée parfois des fonctions avec des noms)

  • function nommée Expression

  • Initialiseur de fonction d'accès (ES5 +)

  • Expression de fonction de flèche (ES2015 +) (qui, comme les expressions de fonction anonymes, ne contient pas de nom explicite et peut créer des fonctions avec des noms)

  • Déclaration de méthode dans l'initialiseur d'objet (ES2015 +)

  • Déclarations de constructeur et de méthode en class (ES2015 +)

Déclaration de fonction

Le premier formulaire est une déclaration de fonction qui ressemble à ceci:

 function x() { console.log('x'); } 

Une déclaration de fonction est une annonce. ce n'est pas une déclaration ou une expression. Donc, vous ne le suivez pas avec ; (bien que ce soit sans danger).

Une déclaration de fonction est traitée lorsque l'exécution entre dans le contexte dans lequel elle apparaît avant l'exécution d' un code d'étape. Un nom est attribué à la fonction créée ( x dans l'exemple ci-dessus) et ce nom est placé dans la zone dans laquelle la déclaration apparaît.

Comme il est traité avant tout code d'étape dans le même contexte, vous pouvez faire quelque chose comme ceci:

 x(); // Works even though it above the declaration function x() { console.log('x'); } 

Avant ES2015, la spécification ne couvrait pas ce que le moteur JavaScript devrait faire si vous avez placé une déclaration de fonction dans une structure de contrôle, par exemple, try , if , switch , tant while , etc. Par exemple:

 if (someCondition) { function foo() { // <===== HERE THERE } // <===== BE DRAGONS } 

Et comme ils sont traités avant d'exécuter le code étape par étape, il est difficile de savoir quoi faire quand ils se trouvent dans la structure de gestion.

Bien que cela n'ait pas été indiqué avant ES2015, il s'agissait d'une extension valide pour prendre en charge les déclarations de fonction dans des blocs. Malheureusement (et inévitablement), différents moteurs ont fait des choses différentes.

À partir de ES2015, la spécification indique quoi faire. En fait, cela donne trois actions distinctes:

  1. Si le navigateur n'est pas en mode libre, le moteur JavaScript devrait faire une chose.
  2. S'il est en mode libre dans un navigateur Web, le moteur JavaScript doit faire autre chose.
  3. Si en mode strict (navigateur ou non), le moteur JavaScript devrait faire une chose de plus.

Les règles pour les modes libres sont délicates, mais en mode strict, les déclarations de fonction dans les blocs sont simples: elles sont locales au bloc (elles ont la portée du bloc, qui est également nouvelle dans ES2015), et elles vont jusqu'au bloc. Donc:

 "use strict"; if (someCondition) { foo(); // Works just fine function foo() { } } console.log(typeof foo); // "undefined" ('foo' is not in scope here // because it not in the same block) 

function expression "anonyme"

La deuxième forme commune s'appelle une expression de fonction anonyme:

 var y = function () { console.log('y'); }; 

Comme toutes les expressions, elle est calculée en fonction de l'exécution du code étape par étape.

Dans ES5, la fonction en cours de création n'a pas de nom (elle est anonyme). Dans ES2015, un nom est attribué à une fonction chaque fois que possible, ce qui le sort du contexte. Dans l'exemple ci-dessus, le nom sera y . Quelque chose comme cela se produit lorsque la fonction est la valeur de l'initialiseur de propriété. (Pour plus d'informations sur le moment où cela se produit et sur les règles, recherchez SetFunctionName dans la spécification - il apparaît partout.)

function nommée Expression

La troisième forme est une expression avec une fonction nommée ("NFE"):

 var z = function w() { console.log('zw') }; 

La fonction créée a son propre nom (dans ce cas, w ). Comme toutes les expressions, ceci est évalué lorsqu'il est réalisé avec une exécution de code pas à pas. Le nom de la fonction n'est pas ajouté à la zone dans laquelle l'expression apparaît. le nom est dans la portée de la fonction elle-même:

 var z = function w() { console.log(typeof w); // "function" }; console.log(typeof w); // "undefined" 

Veuillez noter que les ENF sont souvent une source d’erreurs pour les implémentations de JavaScript. Par exemple, IE8 et les versions antérieures gèrent complètement NFE, créant deux fonctions différentes à deux moments différents. Les premières versions de Safari avaient également des problèmes. La bonne nouvelle est que, dans les versions actuelles des navigateurs (IE9 et les versions plus récentes, Safari actuel), ces problèmes n'existent plus. (Mais malheureusement, au moment d'écrire ces lignes, IE8 est encore largement utilisé et, par conséquent, l'utilisation de NFE avec du code pour Internet dans son ensemble pose toujours problème.)

Initialiseur de fonction d'accès (ES5 +)

Parfois, les fonctions peuvent pénétrer largement inaperçues; Qu'en est-il des fonctions d'accès. Voici un exemple:

 var obj = { value: 0, get f() { return this.value; }, set f(v) { this.value = v; } }; console.log(obj.f); // 0 console.log(typeof obj.f); // "number" 

Veuillez noter que lorsque j'ai utilisé la fonction, je n'ai pas utilisé () ! En effet, il s’agit d’une fonction d’accès à une propriété. Nous obtenons et définissons la propriété de la manière habituelle, mais une fonction est appelée en coulisse.

Vous pouvez également créer des fonctions d'accès à l'aide de Object.defineProperty , Object.defineProperties et du deuxième argument Object.create moins connu.

Expression de la fonction flèche (ES2015 +)

ES2015 nous apporte la fonction de flèche. Voici un exemple:

 var a = [1, 2, 3]; var b = a.map(n => n * 2); console.log(b.join(", ")); // 2, 4, 6 

Voir ce que n => n * 2 est caché dans l'appel de map() ? Ceci est une fonction.

Quelques points sur les fonctions des flèches:

  1. Ils n'ont pas leur propre this . Au lieu de cela, ils ferment this contexte dans lequel ils sont définis. (Ils sont également proches des arguments et, le cas échéant, super .) Cela signifie que this en eux, tout comme this , où ils sont créés et ne peuvent pas être modifiés.

  2. Comme vous l'avez noté ci-dessus, vous n'utilisez pas la function mot clé; au lieu de cela, vous utilisez => .

L'exemple n => n * 2 ci-dessus est l'une de leurs formes. Si vous avez plusieurs arguments pour passer une fonction, vous utilisez des parens:

 var a = [1, 2, 3]; var b = a.map((n, i) => n * i); console.log(b.join(", ")); // 0, 2, 6 

(Rappelez-vous que Array#map passe l'enregistrement en tant que premier argument et l'index en tant que second.)

Dans les deux cas, le corps de la fonction n'est qu'une expression; la valeur de retour de la fonction sera automatiquement le résultat de cette expression (vous n'utilisez pas de return explicite).

Si vous faites plus d'une expression, utilisez {} et un return explicite (si vous devez renvoyer une valeur), comme d'habitude:

 var a = [ {first: "Joe", last: "Bloggs"}, {first: "Albert", last: "Bloggs"}, {first: "Mary", last: "Albright"} ]; a = a.sort((a, b) => { var rv = a.last.localeCompare(b.last); if (rv === 0) { rv = a.first.localeCompare(b.first); } return rv; }); console.log(JSON.stringify(a)); 

Une version sans {... } s'appelle une fonction de flèche avec un corps d'expression ou un corps court. (Également: brève fonction de la flèche.) La fonction avec {... } définissant le corps est la fonction de la flèche avec le corps de la fonction. (En outre: la fonction flèche du verbe.)

Déclaration de méthode dans l'initialiseur d'objet (ES2015 +)

ES2015 autorise un formulaire de déclaration de propriété plus court faisant référence à une fonction appelée définition de méthode; ça ressemble à ça:

 var o = { foo() { } }; 

presque équivalent dans ES5 et les versions antérieures:

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

La différence (sauf la verbosité) est que la méthode peut utiliser super , mais la fonction ne peut pas. Ainsi, par exemple, si vous avez un objet qui définit (par exemple) valueOf utilisant la syntaxe d'une méthode, il peut utiliser super.valueOf() pour obtenir la valeur de Object.prototype.valueOf qui doit être renvoyée (avant de faire quelque chose quelque chose d’autre avec), alors que la version ES5 devrait plutôt remplacer Object.prototype.valueOf.call(this) Object.prototype.valueOf.call(this) .

Cela signifie également que la méthode a une référence à l'objet pour lequel elle a été définie. Par conséquent, si cet objet est temporaire (par exemple, vous le transmettez à Object.assign tant qu'un des objets d'origine), la syntaxe de la méthode peut signifier que l'objet est enregistré. mémoire, sinon, il pourrait être collecté par le ramasse-miettes (si le moteur JavaScript ne détecte pas cette situation et ne le gère pas, si aucune des méthodes n'utilise super ).

Déclarations de constructeur et de méthode en class (ES2015 +)

ES2015 nous fournit la syntaxe de la class , y compris les constructeurs et méthodes déclarés:

 class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; } getFullName() { return this.firstName + " " + this.lastName; } } 

Ci-dessus se trouvent deux déclarations de fonction: une pour le constructeur, appelé Person , et getFullName pour getFullName , fonction attribuée à Person.prototype .

574
04 марта '14 в 16:35 2014-03-04 16:35 La réponse est donnée par TJ Crowder le 04 mars 14 à 16:35 2014-03-04 16:35

Parlant de contexte global, les instructions var et FunctionDeclaration à la fin créent une propriété non supprimable pour l’objet global, mais la valeur des deux peut être remplacée.

La différence subtile entre les deux méthodes est que lorsque le processus d' instanciation de variable est démarré (avant l'exécution réelle du code), tous les identificateurs déclarés avec var seront initialisés avec undefined , et ceux utilisés par FunctionDeclaration seront désormais disponibles, par exemple:

  alert(typeof foo); // 'function', it already available alert(typeof bar); // 'undefined' function foo () {} var bar = function () {}; alert(typeof bar); // 'function' 

L'affectation de la bar FunctionExpression est effectuée avant l'exécution.

La propriété globale créée par FunctionDeclaration peut être écrasée sans problème de la même manière que la valeur d'une variable, par exemple:

  function test () {} test = null; 

Une autre différence évidente entre vos deux exemples réside dans le fait que la première fonction n'a pas de nom, mais que la seconde en possède un, ce qui peut s'avérer très utile lors du débogage (par exemple, la vérification de la pile d'appels).

À propos de votre premier exemple modifié ( foo = function() { alert('hello!'); }; ), Il s'agit d'une tâche non déclarée. Je vous recommande vivement de toujours utiliser le mot-clé var .

Lorsqu'il est attribué sans opérateur var , si l'identificateur de référence n'est pas trouvé dans la chaîne d'étendue, il deviendra une propriété amovible de l'objet global.

De plus, les tâches non déclarées génèrent une erreur ReferenceError sur ECMAScript 5 en mode strict .

A lire impérativement:

Remarque : Cette réponse a été combinée à partir d' une autre question , dans laquelle le principal doute et la mauvaise idée de OP était que les identificateurs déclarés avec FunctionDeclaration ne pouvaient pas être remplacés, ce qui n'est pas le cas.

137
08 авг. la réponse est donnée par CMS 08 aug. 2010-08-08 22:32 '10 à 10:32 2010-08-08 22:32

Les deux extraits de code que vous avez mis là se comporteront de la même manière dans presque tous les cas.

Cependant, la différence de comportement réside dans le fait qu'avec la première option ( var functionOne = function() {} ), cette fonction ne peut être appelée qu'après ce point dans le code.

Dans la deuxième variante ( function functionTwo() ), la fonction est disponible pour le code exécuté ci-dessus, où la fonction est déclarée.

Cela est dû au fait que dans la première variante, la fonction est affectée à la variable foo au moment de l'exécution. Dans la seconde fonction, cet identifiant est attribué à foo lors de l'analyse.

Informations techniques complémentaires

JavaScript a trois façons de définir des fonctions.

  • Dans votre premier fragment, l'expression de la fonction est affichée. Cela est dû à l'utilisation de l'opérateur "function" pour créer une fonction - le résultat de cet opérateur peut être stocké dans n'importe quelle variable ou objet. L'expression de fonction est si puissante. Une expression de fonction est souvent appelée "fonction anonyme" car elle ne doit pas avoir de nom.
  • Le deuxième exemple est une déclaration de fonction. . Pour créer une fonction, utilisez l'opérateur "fonction". La fonction est fournie lors de l'analyse et peut être appelée n'importe où dans cette zone. Vous pouvez l'enregistrer plus tard dans une variable ou un objet.
  • La troisième manière de définir une fonction est le constructeur "Function ()" , qui n'apparaît pas dans le message d'origine. Il n'est pas recommandé de l'utiliser car cela fonctionne de la même manière que eval() , qui a ses propres problèmes.
115
20 апр. la réponse a été donnée par thomasrutter le 20 avril 2010-04-20 07:54 '10 à 7:54 2010-04-20 07:54

Greg répond mieux explication

 functionTwo(); function functionTwo() { } 

Pourquoi pas de bugs? On nous a toujours appris que les expressions sont exécutées de haut en bas (??)

Parce que:

Les déclarations de fonction et les déclarations de variable sont toujours déplacées ( hoisted ) de manière invisible au sommet de leur région à l'aide de l'interpréteur JavaScript. Les paramètres fonctionnels et les noms de >ben cherry

Cela signifie que ce code:

 functionOne(); --------------- var functionOne; | is actually | functionOne(); var functionOne = function(){ | interpreted |--> }; | like | functionOne = function(){ --------------- }; 

Veuillez noter qu’une partie de l’affectation des déclarations n’a pas été soulevée. Seul le nom est élevé.

Mais dans le cas des déclarations de fonction, le corps de la fonction entière sera également levé:

 functionTwo(); --------------- function functionTwo() { | is actually | }; function functionTwo() { | interpreted |--> } | like | functionTwo(); --------------- 
96
09 авг. la réponse est donnée simple_human 09 août. 2014-08-09 05:45 '14 à 5:45 am 2014-08-09 05:45

D'autres commentateurs ont déjà examiné la différence sémantique entre les deux options énumérées ci-dessus. Je voudrais souligner une différence de style: seule une variante de "destination" peut établir la propriété d'un autre objet.

Je crée souvent des modules JavaScript avec ce modèle:

 (function(){ var exports = {}; function privateUtil() { ... } exports.publicUtil = function() { ... }; return exports; })(); 

En utilisant ce modèle, vos fonctions publiques utiliseront la destination, tandis que vos fonctions privées utiliseront l’annonce.

(Notez également que l'affectation doit contenir un point-virgule après l'instruction, alors que l'annonce l'interdit.)

87
03 марта '11 в 22:19 2011-03-03 22:19 a répondu à Sean McMillan le 03 mars '11 à 22:19 2011-03-03 22:19

Une illustration du meilleur moment pour utiliser la première méthode pour la seconde consiste à éviter de surcharger les fonctions des définitions précédentes.

Avec

 if (condition){ function myfunction(){ // Some code } } 

cette définition, myfunction remplacera toute définition précédente, car elle sera exécutée lors de l'analyse.

Pendant que

 if (condition){ var myfunction = function (){ // Some code } } 

fait le travail correct de définition de myfunction uniquement lorsque la condition est exécutée.

73
29 марта '13 в 16:26 2013-03-29 16:26 Réponse donnée par Mbengue Assane le 29 mars '13 à 16:26 2013-03-29 16:26

Une raison importante est l'ajout d'une et une seule variable en tant que «racine» de votre espace de noms ...

 var MyNamespace = {} MyNamespace.foo= function() { } 

ou

 var MyNamespace = { foo: function() { }, ... } 

Il existe de nombreuses méthodes pour l'espace de noms. Cela devient plus important avec les nombreux modules JavaScript disponibles.

Voir aussi Comment déclarer un espace de noms en javascript?

59
08 авг. la réponse est donnée par Rob 08 août. 2010-08-08 22:44 '10 à 22h44 2010-08-08 22:44

Hoisting - это действие интерпретаторов JavaScript для перемещения всех объявлений переменных и функций в начало текущей объем.