Question var functionName = fonction () {} vs fonction nomFonction () {}


J'ai récemment commencé à conserver le code JavaScript de quelqu'un d'autre. Je corrige des bugs, j'ajoute des fonctionnalités et j'essaie aussi de ranger le code et de le rendre plus cohérent.

Le développeur précédent utilise deux manières de déclarer des fonctions et je ne peux pas travailler s'il y a une raison derrière cela ou pas.

Les deux façons 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 at-il quelque chose qui peut être fait avec une méthode qui ne peut pas être fait avec l'autre?


6052
2017-12-03 11:31


origine


Réponses:


La différence est que functionOne est une expression de fonction et donc seulement définie lorsque cette ligne est atteinte, alors que functionTwo est une déclaration de fonction et est définie dès que la fonction ou le script qui l'entoure est exécuté levage).

Par exemple, une expression de fonction:

// TypeError: functionOne is not a function
functionOne();

var functionOne = function() {
  console.log("Hello!");
};

Et, une déclaration de fonction:

// Outputs: "Hello!"
functionTwo();

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

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

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

Ce qui précède définit réellement functionThree indépendamment de testvaleur de - à moins use strict est en vigueur, auquel cas il provoque simplement une erreur.


4489
2017-12-03 11:37



D'abord je veux corriger Greg: function abc(){} est délimité aussi - le nom abc est défini dans la portée 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 mais Internet Explorer - ne comptez pas sur le fait qu'il soit défini. 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 souhaitez utiliser des fonctions d'alias 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 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 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 long nom non conflictuel pour les 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 un nom externe, mais ce sera trop lourd (et plus lent).

(Une autre façon de se référer est d'utiliser arguments.callee, qui est encore relativement long, et non supporté en mode strict.)

Au fond, JavaScript traite les deux instructions différemment. Ceci est une déclaration de fonction:

function abc(){}

abc ici est défini partout dans le champ d'application actuel:

// 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 hissé à travers un return déclaration:

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

C'est 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 la fonction est la vraie 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 la fonction comme

var abc = function(){};

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

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 d'étendues. Ce style de définition est résilient même lorsqu'il est utilisé à l'intérieur eval(). Alors que la définition

function abc(){};

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


1804
2017-12-03 17:43



Voici le résumé sur les formulaires standard qui créent des fonctions: (À l'origine écrit pour une autre question, mais adapté après avoir été déplacé dans la question canonique.)

Termes:

La liste rapide:

  • Déclaration de fonction

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

  • Nommé function Expression

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

  • Expression de la fonction flèche (ES2015 +) (qui, comme les expressions de fonction anonymes, n'impliquent pas un nom explicite et peuvent 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 dans class (ES2015 +)

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 le suivez pas avec un ; (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 étape par étape est exécuté. La fonction créée crée un nom propre (x dans l'exemple ci-dessus), et ce nom est placé dans le champ d'application dans lequel la déclaration apparaît.

Comme il est traité avant tout code étape par étape dans le même contexte, vous pouvez effectuer les opérations suivantes:

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

Jusqu'à ES2015, la spécification ne couvre pas ce qu'un moteur JavaScript devrait faire si vous mettez une déclaration de fonction dans une structure de contrôle comme try, if, switch, while, etc., comme ceci:

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

Et puisqu'ils sont traités avant le code étape par étape est exécuté, il est difficile de savoir quoi faire quand ils sont dans une structure de contrôle.

Bien que faire cela n'était pas spécifié jusqu'à ES2015, c'était un extension autorisée pour supporter les déclarations de fonction dans les blocs. Malheureusement (et inévitablement), différents moteurs ont fait des choses différentes.

À partir de ES2015, la spécification indique ce qu'il faut faire. En fait, cela donne trois choses distinctes à faire:

  1. Si en mode lâche ne pas sur un navigateur web, le moteur JavaScript est censé faire une chose
  2. Si en mode loose sur un navigateur web, le moteur JavaScript est supposé faire autre chose
  3. Si dans strict mode (navigateur ou pas), le moteur JavaScript est supposé faire encore autre chose

Les règles pour les modes lâches sont difficiles, mais dans strict mode, les déclarations de fonction dans les blocs sont faciles: elles sont locales au bloc (elles ont bloquer la portée, qui est également nouveau dans ES2015), et ils sont hissés au sommet du bloc. Alors:

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

"Anonyme" function Expression

La deuxième forme commune est appelée expression de fonction anonyme:

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

Comme toutes les expressions, il est évalué lorsqu'il est atteint dans l'exécution pas-à-pas du code.

Dans ES5, la fonction que cela crée n'a pas de nom (c'est anonyme). Dans ES2015, un nom est attribué à la fonction si possible en l'inférant du contexte. Dans l'exemple ci-dessus, le nom serait y. Quelque chose de similaire est fait lorsque la fonction est la valeur d'un initialiseur de propriété. (Pour plus de détails sur les circonstances et les règles, recherchez SetFunctionName dans le la spécification- il semble partout le lieu.)

Nommé function Expression

La troisième forme est un expression de la fonction nommée ("NFE"):

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

La fonction que cela crée a un nom propre (w dans ce cas). Comme toutes les expressions, ceci est évalué quand il est atteint dans l'exécution pas-à-pas du code. Le nom de la fonction est ne 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 NFE ont souvent été une source de bogues pour les implémentations JavaScript. IE8 et les versions antérieures, par exemple, gèrent les NFE complètement incorrectement, créant deux fonctions différentes à deux moments différents. Les premières versions de Safari avaient également des problèmes. Les bonnes nouvelles sont que les versions actuelles des navigateurs (IE9 et plus, Safari actuel) n'ont plus ces problèmes. (Mais au moment d'écrire ces lignes, malheureusement, IE8 reste largement utilisé, et donc utiliser les ENF avec du code pour le web en général est toujours problématique.)

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

Parfois, les fonctions peuvent se faufiler en grande partie inaperçues; c'est le cas avec fonctions d'accesseur. 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"

Notez que lorsque j'ai utilisé la fonction, je n'ai pas utilisé ()! C'est parce que c'est un fonction d'accesseur pour une propriété. Nous obtenons et définissons la propriété de la manière normale, mais dans les coulisses, la fonction est appelée.

Vous pouvez également créer des fonctions d'accesseur avec Object.defineProperty, Object.defineProperties, et le second argument moins connu Object.create.

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

Regarde ça n => n * 2 chose cachée dans le map() appel? C'est une fonction.

Un couple de choses sur les fonctions de la flèche:

  1. Ils n'ont pas leur propre this. Au lieu de cela, ils fermer la this du contexte où ils sont définis. (Ils ferment aussi arguments et, le cas échéant, super.) Cela signifie que le this en leur sein est le même que le this où ils sont créés et ne peuvent pas être modifiés.

  2. Comme vous l'avez remarqué avec ce qui précède, vous n'utilisez pas le mot-clé function; Au lieu de cela, vous utilisez =>.

le n => n * 2 exemple ci-dessus est une forme d'entre eux. Si vous avez plusieurs arguments pour passer la fonction, vous utilisez 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'entrée comme premier argument, et l'index comme deuxième argument.)

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).

Si vous faites plus qu'une simple expression, utilisez {} et un explicite return (si vous avez besoin de 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));

La version sans { ... } est appelée une fonction de flèche avec un corps d'expression ou corps concis. (Aussi un concis fonction de flèche.) Celui avec { ... } la définition du corps est une fonction de flèche avec un corps de fonction. (Aussi un verbeux fonction de flèche.)

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

ES2015 permet une forme plus courte de déclaration d'une propriété qui référence une fonction; ça ressemble à ceci:

var o = {
    foo() {
    }
};

l'équivalent en ES5 et plus tôt serait:

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

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

ES2015 nous amène class syntaxe, y compris les constructeurs déclarés et les méthodes:

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    getFullName() {
        return this.firstName + " " + this.lastName;
    }
}

Il y a deux déclarations de fonction ci-dessus: Une pour le constructeur, qui obtient le nom Person, et un pour getFullName, qui est une fonction assignée à Person.prototype.


544
2018-03-04 13:35



Parlant du contexte mondial, les deux, le var déclaration et un FunctionDeclaration à la fin va créer un non-délétable propriété sur l'objet global, mais la valeur des deux peut être écrasé.

La différence subtile entre les deux façons est que lorsque le Instanciation variable processus exécute (avant l'exécution du code) tous les identificateurs déclarés avec var sera initialisé avec undefinedet ceux utilisés par FunctionDeclarationsera disponible depuis ce moment, par exemple:

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

L'attribution du bar  FunctionExpression a lieu jusqu'à l'exécution.

Une propriété globale créée par un FunctionDeclaration peut être écrasé sans aucun 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, mais la seconde l'a, ce qui peut être très utile lors du débogage (c'est-à-dire l'inspection d'une pile d'appels).

A propos de votre premier exemple modifié (foo = function() { alert('hello!'); };), c'est une mission non déclarée, je vous encourage fortement à toujours utiliser var mot-clé.

Avec une affectation, sans var déclaration, si l'identificateur référencé n'est pas trouvé dans la chaîne de portée, il deviendra un délétable propriété de l'objet global.

En outre, les affectations non déclarées jettent un ReferenceError sur ECMAScript 5 sous Mode strict.

À lire absolument:

Remarque: Cette réponse a été fusionnée à partir de une autre question, dans lequel le doute majeur et l'idée fausse de l'OP était que les identifiants déclarés avec un FunctionDeclaration, ne peut pas être écrasé, ce qui n'est pas le cas.


133
2017-08-08 19:32



Les deux extraits de code que vous avez publiés là-bas, pour presque tous les buts, se comportent de la même manière.

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

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

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

Plus d'informations techniques

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

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

111
2018-04-20 04:54



Une meilleure explication à La réponse de Greg

functionTwo();
function functionTwo() {
}

Pourquoi pas d'erreur? On nous a toujours enseigné que les expressions sont exécutées de haut en bas (??)

Car:

Les déclarations de fonction et les déclarations de variables sont toujours déplacées (hoisted) invisiblement au sommet de leur portée contenant par l'interpréteur JavaScript. Les paramètres de fonction et les noms définis par la langue sont, de toute évidence, déjà présents. ben cerise

Cela signifie que le code comme ceci:

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

Notez que la portion d'affectation des déclarations n'a pas été levée. Seul le nom est hissé.

Mais dans le cas des déclarations de fonction, le corps entier de la fonction sera également hissé:

functionTwo();              ---------------      function functionTwo() {
                            | is actually |      };
function functionTwo() {    | interpreted |-->
}                           |    like     |      functionTwo();
                            ---------------

91
2017-08-09 02:45



D'autres commentateurs ont déjà couvert la différence sémantique des deux variantes ci-dessus. Je voulais noter une différence stylistique: Seule la variation "affectation" peut définir une propriété d'un autre objet.

Je construis souvent des modules JavaScript avec un motif comme celui-ci:

(function(){
    var exports = {};

    function privateUtil() {
            ...
    }

    exports.publicUtil = function() {
            ...
    };

    return exports;
})();

Avec ce modèle, vos fonctions publiques utilisent toutes l'affectation, tandis que vos fonctions privées utilisent la déclaration.

(Notez également que l'assignation devrait exiger un point-virgule après l'instruction, tandis que la déclaration l'interdit.)


83
2018-03-03 19:19



Une illustration de quand préférer la première méthode à la seconde est quand vous devez éviter de surcharger les définitions précédentes d'une fonction.

Avec

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

, cette définition de myfunction remplacera toute définition précédente, car elle sera effectuée au moment de l'analyse.

Tandis que

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

fait le bon travail de définition myfunction seulement quand condition est rencontré.


68
2018-03-29 13:26



Une raison importante est d'ajouter une et une seule variable en tant que "Root" de votre espace de noms ...

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

}

ou

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

Il existe de nombreuses techniques pour le nommage. C'est devenu plus important avec la pléthore de modules JavaScript disponibles.

Regarde aussi Comment puis-je déclarer un espace de noms en JavaScript?


55
2017-08-08 19:44