7258 votes

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

J'ai récemment commencé à maintenir le code JavaScript de quelqu'un d'autre. Je corrige des bogues, j'ajoute des fonctionnalités et j'essaie également de mettre de l'ordre dans le code et de le rendre plus cohérent.

Le développeur précédent utilise deux façons de déclarer les fonctions et je n'arrive pas à savoir s'il y a une raison derrière cela ou non.

Les deux voies 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 ? Y a-t-il quelque chose qui peut être fait avec une méthode et qui ne peut pas être fait avec l'autre ?

5340voto

Greg Points 132247

La différence est que functionOne est définie au moment de l'exécution, alors que functionTwo est défini au moment de l'analyse syntaxique pour un bloc script. Par exemple :

<script>
  // Error
  functionOne();

  var functionOne = function() {
  };
</script>

<script>
  // No error
  functionTwo();

  function functionTwo() {
  }
</script>

<script>
  "use strict";
  if (test) {
     // Error
     function functionThree() { doSomething(); }
  }
  else {
     // Error
     function functionThree() { doSomethingElse(); }
  }
</script>

2025voto

Eugene Lazutkin Points 22414

D'abord, je veux corriger Greg : function abc(){} a également un champ d'application - le nom abc est définie dans l'étendue où cette définition est rencontrée. Exemple :

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

Deuxièmement, il est possible de combiner les deux styles :

var xyz = function abc(){};

xyz va être défini comme d'habitude, abc est indéfini dans tous les navigateurs sauf IE - ne comptez pas sur sa définition. Mais il sera défini à l'intérieur de son corps :

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

Si vous voulez aliaser des fonctions sur tous les navigateurs, utilisez ce type de déclaration :

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

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

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

Une raison convaincante d'utiliser le style combiné est l'attribut "name" des objets fonctionnels ( non supporté par IE ). En gros, lorsque vous définissez une fonction comme ceci :

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

son nom est automatiquement attribué. Mais lorsque vous le définissez comme ceci :

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

son nom est vide - nous avons créé une fonction anonyme et l'avons assignée à une variable.

Une autre bonne raison d'utiliser le style combiné est d'utiliser un nom interne court pour se référer à lui-même, tout en fournissant un nom long non contradictoire pour les utilisateurs externes :

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

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

(Une autre façon de se référer à lui-même est d'utiliser arguments.callee ce qui est encore relativement long, et n'est pas supporté dans le mode strict).

Au fond, JavaScript traite les deux déclarations différemment. Il s'agit d'une déclaration de fonction :

function abc(){}

abc ici est défini partout dans la portée actuelle :

// we can call it here
abc(); // works
// yet it is defined down there
function abc(){}
// we can call it again
abc(); // works

Il s'agit d'une expression de fonction :

var xyz = function(){};

xyz ici est défini à partir du point d'affectation :

// 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 fonction par rapport à l'expression de fonction est la véritable raison pour laquelle il existe 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 de cette façon, je peux contrôler la visibilité. Quand je définis la fonction comme ça :

var abc = function(){};

Je sais que j'ai défini la fonction localement. Quand je définis la fonction comme ça :

abc = function(){};

Je sais que je l'ai défini globalement à condition que je n'ai pas défini abc n'importe où dans la chaîne des scopes. Ce style de définition est résilient même lorsqu'il est utilisé à l'intérieur de eval(). Bien que cette définition :

function abc(){};

dépend du contexte et peut vous laisser deviner où il est réellement défini, notamment dans le cas de eval() - la réponse est : cela dépend du navigateur.

691voto

T.J. Crowder Points 285826

Voici le récapitulatif des formulaires standard qui créent des fonctions : (Initialement écrit pour une autre question, mais adapté après avoir été déplacé dans la question canonique).

Déclaration de fonction

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

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

Une déclaration de fonction est un déclaration ce n'est pas une déclaration ou une expression. En tant que tel, vous ne devez pas le faire suivre d'une balise ; (bien que cela soit inoffensif).

Une déclaration de fonction est traitée lorsque l'exécution entre dans le contexte dans lequel elle apparaît, avant tout code pas à pas est exécuté. La fonction qu'il crée reçoit un nom propre ( x dans l'exemple ci-dessus), et ce nom est placé dans la portée dans laquelle la déclaration apparaît.

Comme il est traité avant tout code étape par étape dans le même contexte, vous pouvez faire des choses comme ceci :

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

De plus, comme il ne fait pas partie de l'exécution pas à pas du code, vous ne pouvez pas le placer à l'intérieur d'une structure de contrôle telle que try , if , switch , while etc.

if (someCondition) {
    function foo() {    // <===== INVALID AND WILL FAIL ON
    }                   //        MANY ENGINES
}

Certains moteurs traiteront l'expression ci-dessus même si elle n'est pas valide, en la réécrivant comme une expression de fonction à la volée. Il est question d'ajouter un déclaration de fonction à la prochaine spécification (ECMAScript6) pour codifier cela. Mais avec les moteurs actuels, cela ne fonctionnera pas de manière fiable ; ne le faites pas.

Fonction anonyme Expression

La deuxième forme la plus courante est appelée expression de fonction anonyme :

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

La fonction ainsi créée n'a pas de nom (elle est anonyme). Comme toutes les expressions, elle est évaluée lorsqu'elle est atteinte dans l'exécution pas à pas du code.

Je dois noter que le projet actuel de la suivant La version de la norme JavaScript, ECMAScript6, attribue en fait un nom à cette fonction en le déduisant du contexte. Dans ce cas, le nom serait y . Le moteur JavaScript de Mozilla (Firefox) le fait déjà, et il sera dans la prochaine spécification. En gros, chaque fois que l'analyseur peut faire une supposition raisonnable, comme c'est le cas ci-dessus, il le fera (une fois que les moteurs feront cette nouvelle chose).

Expression de la fonction nommée

Le troisième formulaire est un expression de fonction nommée ("NFE") :

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

La fonction ainsi créée a un nom propre ( w dans ce cas). Comme toutes les expressions, celle-ci est évaluée lorsqu'elle est atteinte dans l'exécution pas à pas du code. Le nom de la fonction est pas ajouté à la portée 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"

Notez que les ENF ont souvent été une source de bogues pour les implémentations JavaScript. IE8 et les versions antérieures, par exemple, gèrent les ENF de la manière suivante complètement faux en créant deux fonctions différentes à deux moments différents. Les premières versions de Safari présentaient également des problèmes. La bonne nouvelle est que les versions actuelles des navigateurs (IE9 et plus, Safari actuel) n'ont plus ces problèmes. (Mais à l'heure où nous écrivons ces lignes, IE8 reste malheureusement largement utilisé, et l'utilisation des ENF avec du code pour le web en général reste donc problématique).

162voto

CMS Points 315406

En ce qui concerne le contexte mondial, tant les var et une FunctionDeclaration à la fin créera un non supprimable sur l'objet global, mais la valeur des deux propriétés peuvent être écrasés .

La différence subtile entre les deux façons est que lorsque le Instanciation des variables Le processus s'exécute (avant l'exécution effective du code), tous les identificateurs déclarés avec la fonction var sera initialisé avec undefined et ceux utilisés par l FunctionDeclaration seront disponibles à partir de ce moment-là, par exemple :

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

L'affectation de la bar FunctionExpression a lieu jusqu'au moment de l'exécution.

Une propriété globale créée par un FunctionDeclaration peut être écrasé sans problème, tout comme une valeur variable, par exemple :

 function test () {}
 test = null;

Une autre différence évidente entre vos deux exemples est que la première fonction n'a pas de nom, alors que la seconde en a un, ce qui peut être très utile lors du débogage (c'est-à-dire l'inspection d'une pile d'appels).

À propos de votre premier exemple édité ( foo = function() { alert('hello!'); }; ), il s'agit d'une affectation non déclarée, je vous encourage vivement à toujours utiliser l'option var mot-clé.

Avec une affectation, sans le var si l'identifiant référencé n'est pas trouvé dans la chaîne de portée, il deviendra un supprimable de l'objet global.

De plus, les affectations non déclarées génèrent une erreur de type ReferenceError sur ECMAScript 5 sous Mode strict .

A lire absolument :

Note : Cette réponse a été fusionnée avec une autre question dans lequel le principal doute et l'idée fausse du PO était que les identificateurs déclarés avec une balise FunctionDeclaration ne pouvait pas être écrasé, ce qui n'est pas le cas.

137voto

thomasrutter Points 42905

Les deux extraits de code que vous avez postés se comporteront, dans la plupart des cas, de la même manière.

Cependant, la différence de comportement est qu'avec la première variante, cette fonction ne peut être appelée qu'après ce point du code.

Avec la deuxième variante, la fonction est disponible pour le code qui s'exécute au-dessus de l'endroit où la fonction est déclarée.

En effet, avec la première variante, la fonction est affectée à la variable foo au moment de l'exécution. Dans la seconde, la fonction est affectée à cet identifiant foo au moment de l'analyse syntaxique.

Plus d'informations techniques

Javascript propose trois façons de définir les fonctions.

  1. Votre premier extrait montre un fonction expression . Cela implique l'utilisation du "opérateur "fonction pour créer une fonction - le résultat de cet opérateur peut être stocké dans n'importe quelle variable ou propriété d'objet. L'expression de fonction est puissante de cette façon. L'expression de fonction est souvent appelée "fonction anonyme" car elle n'a pas besoin d'avoir un nom,
  2. Votre deuxième exemple est un déclaration de fonction . Cela utilise le "Déclaration de "fonction pour créer une fonction. La fonction est rendue disponible au moment de l'analyse syntaxique et peut être appelée n'importe où dans cette portée. Vous pouvez toujours la stocker ultérieurement dans une variable ou une propriété d'objet.
  3. La troisième façon de définir une fonction est la "Constructeur "Function() qui n'apparaît pas dans votre message original. Il n'est pas recommandé d'utiliser cette méthode car elle fonctionne de la même manière que eval(), qui a ses propres problèmes.

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