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.)
Termes:
La liste rapide:
Déclaration de fonction
function
Expression "anonyme" (qui malgré le terme, crée parfois des fonctions avec des noms)
function
Expression nommée
Initialiseur de fonction d'accesseur (ES5 +)
Expression de fonction de flèche (ES2015 +) (qui, comme les expressions de fonction anonymes, n'implique pas de nom explicite et peut néanmoins 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 une déclaration de fonction , qui ressemble à ceci:
function x() {
console.log('x');
}
Une déclaration de fonction est une 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 l' exécution de tout code étape par étape. La fonction qu'elle 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.
Parce qu'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');
}
Jusqu'à ES2015, la spécification ne couvre pas ce que l' intérieur d' une structure de contrôle comme un moteur JavaScript doit faire si vous mettez une déclaration de fonction try
, if
, switch
, while
, etc., comme ceci:
if (someCondition) {
function foo() { // <===== HERE THERE
} // <===== BE DRAGONS
}
Et comme ils sont traités avant l' exécution du code étape par étape, il est difficile de savoir quoi faire lorsqu'ils sont dans une structure de contrôle.
Bien que cela n'ait pas été spécifié avant ES2015, c'était une extension autorisée pour prendre en charge les déclarations de fonction dans les blocs. Malheureusement (et inévitablement), différents moteurs ont fait des choses différentes.
Depuis ES2015, la spécification indique ce qu'il faut faire. En fait, cela donne trois choses distinctes à faire:
- Si en mode lâche pas sur un navigateur Web, le moteur JavaScript est censé faire une chose
- Si en mode lâche sur un navigateur Web, le moteur JavaScript est censé faire autre chose
- Si en mode strict (navigateur ou non), le moteur JavaScript est censé faire encore autre chose
Les règles pour les modes lâches sont délicates, mais en mode strict , les déclarations de fonctions dans les blocs sont faciles: elles sont locales au bloc (elles ont une portée de bloc , qui est également nouvelle dans ES2015), et elles sont hissées vers le haut du 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's not in the same block)
function
Expression "anonyme"
La deuxième forme courante est appelée une expression de fonction anonyme :
var y = function () {
console.log('y');
};
Comme toutes les expressions, il est évalué lorsqu'il est atteint lors de l'exécution pas à pas du code.
Dans ES5, la fonction ainsi créée n'a pas de nom (elle est anonyme). Dans ES2015, la fonction se voit attribuer un nom si possible en le déduisant 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 le moment où cela se produit et les règles, recherchez SetFunctionName
dans la spécification - elle apparaît partout .)
function
Expression nommée
La troisième forme est une expression de fonction nommée ("NFE"):
var z = function w() {
console.log('zw')
};
La fonction ainsi créée porte un nom propre ( w
dans ce cas). Comme toutes les expressions, cela est évalué lorsqu'il est atteint lors de l'exécution pas à pas du code. Le nom de la fonction n'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 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 de manière complètement incorrecte , 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 les versions actuelles des navigateurs (IE9 et plus, Safari actuel) n'ont plus ces problèmes. (Mais à ce jour, malheureusement, IE8 reste largement utilisé, et donc l'utilisation d'ENF avec du code pour le Web en général reste problématique.)
Initialiseur de fonction d'accesseur (ES5 +)
Parfois, les fonctions peuvent passer inaperçues en grande partie; c'est le cas des 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 ne l'ai pas utilisée ()
! C'est parce que c'est une fonction d'accesseur pour une propriété. Nous obtenons et définissons la propriété de la manière normale, mais en arrière-plan, la fonction est appelée.
Vous pouvez également créer des accesseurs fonctions avec Object.defineProperty
, Object.defineProperties
et le second argument moins connu pour Object.create
.
Expression de la fonction flèche (ES2015 +)
ES2015 nous apporte la fonction flèche . Voici un exemple:
var a = [1, 2, 3];
var b = a.map(n => n * 2);
console.log(b.join(", ")); // 2, 4, 6
Vous voyez cette n => n * 2
chose qui se cache dans l' map()
appel? C'est une fonction.
Quelques choses sur les fonctions fléchées:
Ils n'ont pas les leurs this
. Au lieu de cela, ils près sur le this
du contexte dans lequel elles sont définies. (Ils se ferment également arguments
et, le cas échéant,. super
) Cela signifie que l' this
intérieur d'eux est le même que l' this
endroit où ils ont été créés et ne peut pas être modifié.
Comme vous l'avez remarqué avec ce qui précède, vous n'utilisez pas le mot-clé function
; à la place, vous utilisez =>
.
L' n => n * 2
exemple ci-dessus en est une forme. Si vous avez plusieurs arguments pour passer la 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
(N'oubliez pas que Array#map
passe l'entrée comme premier argument et l'index comme deuxième.)
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 d'explicite return
).
Si vous faites plus qu'une simple expression, utilisez {}
et une explicite return
(si vous devez retourner 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 fonction de flèche avec un corps d'expression ou un corps concis . (Aussi: Une fonction de flèche concise .) Celui qui { ... }
définit le corps est une fonction de flèche avec un corps de fonction . (Aussi: Une fonction de flèche verbeuse .)
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 fait référence à une fonction appelée définition de méthode ; cela ressemble à ceci:
var o = {
foo() {
}
};
le quasi-équivalent dans ES5 et les versions antérieures serait:
var o = {
foo: function foo() {
}
};
la différence (autre que la verbosité) est qu'une méthode peut utiliser super
, mais pas une fonction. Ainsi, par exemple, si vous aviez un objet qui définissait (disons) en valueOf
utilisant la syntaxe de la méthode, il pourrait utiliser super.valueOf()
pour obtenir la valeur Object.prototype.valueOf
serait retourné (avant de faire probablement autre chose avec lui), alors que la version ES5 devrait le faire à la Object.prototype.valueOf.call(this)
place.
Cela signifie également que la méthode a une référence à l'objet sur lequel elle a été définie, donc si cet objet est temporaire (par exemple, vous le transmettez Object.assign
comme l'un des objets source), la syntaxe de la méthode pourrait signifier que l'objet est conservé en mémoire alors qu'il aurait pu être récupéré (si le moteur JavaScript ne détecte pas cette situation et ne la gère si aucune des méthodes ne l'utilise super
).
Déclarations de constructeur et de méthode dans class
(ES2015 +)
ES2015 nous apporte la class
syntaxe, 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;
}
}
Il y a deux déclarations de fonction ci-dessus: une pour le constructeur, qui obtient le nom Person
, et une pour getFullName
, qui est une fonction affectée à Person.prototype
.