Nous publions aujourd'hui la quatrième partie de la traduction du manuel JavaScript, consacrée aux fonctions.
→
Partie 1: premier programme, fonctionnalités linguistiques, normes→
Partie 2: style de code et structure du programme→
Partie 3: variables, types de données, expressions, objets→
Partie 4: caractéristiques→
Partie 5: tableaux et boucles→
Partie 6: exceptions, points-virgules, littéraux génériques→
Partie 7: mode strict, ce mot-clé, événements, modules, calculs mathématiques→
Partie 8: Présentation des fonctionnalités d'ES6→
Partie 9: Présentation des normes ES7, ES8 et ES9
Fonctions JavaScript
Parlons des fonctions en JavaScript, examinons-les en général et considérons les détails à leur sujet, dont la connaissance vous permettra de les utiliser efficacement.
Une fonction est un bloc de code indépendant qui, une fois déclaré, peut être appelé autant de fois que nécessaire. Une fonction peut, bien que non nécessaire, accepter des paramètres. Les fonctions renvoient une seule valeur.
Les fonctions en JavaScript sont des objets, ou plutôt, ce sont des objets de type
Function
. Leur principale différence avec les objets ordinaires, en leur donnant les capacités exceptionnelles qu'ils possèdent, est que les fonctions peuvent être appelées.
De plus, les fonctions en JavaScript sont appelées «fonctions de première classe» car elles peuvent être affectées à des variables, elles peuvent être passées à d'autres fonctions en tant qu'arguments et elles peuvent être renvoyées à partir d'autres fonctions.
Premièrement, nous considérons les caractéristiques du travail avec les fonctions et les constructions syntaxiques correspondantes qui existaient dans le langage avant l'avènement de la norme ES6 et qui sont toujours pertinentes.
Voici à quoi ressemble une déclaration de fonction.
function doSomething(foo) {
De nos jours, ces fonctions sont dites "normales", ce qui les distingue des fonctions "flèches" apparues dans ES6.
Vous pouvez affecter une fonction à une variable ou une constante. Une telle construction est appelée une expression de fonction.
const doSomething = function(foo) {
Vous pouvez remarquer que dans l'exemple ci-dessus, la fonction est affectée à une constante, mais elle-même n'a pas de nom. Ces fonctions sont appelées anonymes. Des fonctions similaires peuvent recevoir des noms. Dans ce cas, nous parlons d'une expression de fonction nommée (expression de fonction nommée).
const doSomething = function doSomFn(foo) {
L'utilisation de telles expressions augmente la commodité du débogage (dans les messages d'erreur où la trace de pile est effectuée, le nom de la fonction est visible). Le nom de la fonction dans une expression fonctionnelle peut également être nécessaire pour que la fonction puisse s'appeler, ce qui est indispensable pour implémenter des algorithmes récursifs.
Dans la norme ES6, des fonctions flèches sont apparues, qui sont particulièrement pratiques à utiliser sous la forme de «fonctions en ligne» - comme arguments passés à d'autres fonctions (rappels).
const doSomething = foo => {
Les fonctions fléchées, en plus du fait que les structures utilisées pour les déclarer, sont plus compactes que l'utilisation de fonctions ordinaires, elles en diffèrent par certaines caractéristiques importantes, dont nous parlerons ci-dessous.
Paramètres de fonction
Les paramètres sont des variables qui sont définies au stade de la déclaration d'une fonction et contiendront les valeurs qui lui sont transmises (ces valeurs sont appelées arguments). Les fonctions en JavaScript peuvent n'avoir aucun paramètre ou avoir un ou plusieurs paramètres.
const doSomething = () => {
Voici quelques exemples de fonctions fléchées.
À partir de la norme ES6, les fonctions peuvent avoir ce que l'on appelle des «paramètres par défaut».
const doSomething = (foo = 1, bar = 'hey') => {
Ils représentent des valeurs standard qui sont définies par les paramètres des fonctions si, lors de son appel, les valeurs de certains paramètres ne sont pas définies. Par exemple, la fonction illustrée ci-dessus peut être appelée à la fois en lui transmettant les deux paramètres qu'elle reçoit et par d'autres méthodes.
doSomething(3) doSomething()
Dans ES8, il est devenu possible de mettre une virgule après le dernier argument d'une fonction (c'est ce qu'on appelle une virgule de fin). Cette fonctionnalité vous permet d'augmenter la commodité de l'édition de code lors de l'utilisation de systèmes de contrôle de version pendant le développement du programme. Des détails à ce sujet peuvent être trouvés
ici et
ici .
Les arguments passés aux fonctions peuvent être représentés sous forme de tableaux. Afin d'analyser ces arguments, vous pouvez utiliser un opérateur qui ressemble à trois points (il s'agit de ce que l'on appelle "l'opérateur d'extension" ou "l'opérateur d'étalement"). Voici à quoi ça ressemble.
const doSomething = (foo = 1, bar = 'hey') => {
Si les fonctions doivent prendre de nombreux paramètres, il peut être difficile de se souvenir de l'ordre de leur séquence. Dans de tels cas, des objets avec des paramètres et des possibilités de déstructuration d'objets ES6 sont utilisés.
const doSomething = ({ foo = 1, bar = 'hey' }) => {
Cette technique permet, en décrivant les paramètres sous forme de propriétés d'objet et en passant la fonction à l'objet, d'obtenir l'accès de la fonction aux paramètres par leurs noms sans utiliser de constructions supplémentaires. En savoir plus sur cette technique
ici .
Valeurs renvoyées par les fonctions
Toutes les fonctions renvoient une certaine valeur. Si la commande de retour n'est pas explicitement spécifiée, la fonction renverra
undefined
.
const doSomething = (foo = 1, bar = 'hey') => {
L'exécution de la fonction se termine soit après l'exécution de tout le code qu'elle contient, soit après que le mot-clé
return
été rencontré dans le code. Lorsque ce mot-clé est rencontré dans une fonction, son opération est terminée et le contrôle est transféré à l'endroit d'où la fonction a été appelée.
Si après le mot-clé
return
vous spécifiez une certaine valeur, cette valeur revient à la place de l'appel de fonction suite à l'exécution de cette fonction.
const doSomething = () => { return 'test' } const result = doSomething()
Une seule valeur peut être renvoyée par une fonction. Afin de pouvoir renvoyer plusieurs valeurs, vous pouvez les renvoyer sous forme d'objet à l'aide d'un littéral d'objet ou sous forme de tableau, et lors de l'appel d'une fonction, utilisez la construction d'affectation destructrice. Les noms des paramètres sont enregistrés. Dans le même temps, si vous devez travailler avec un objet ou un tableau renvoyé par une fonction, notamment sous la forme d'un objet ou d'un tableau, vous pouvez vous passer de l'affectation destructrice.
const doSomething = () => { return ['Roger', 6] } const [ name, age ] = doSomething() console.log(name, age)
La construction
const [ name, age ] = doSomething()
peut être lue comme suit: "déclarez le
name
et les constantes d'
age
et affectez-leur les valeurs des éléments du tableau que la fonction renverra."
Voici à quoi ressemble la même chose en utilisant un objet.
const doSomething = () => { return {name: 'Roger', age: 6} } const { name, age } = doSomething() console.log(name, age)
Fonctions imbriquées
Les fonctions peuvent être déclarées à l'intérieur d'autres fonctions.
const doSomething = () => { const doSomethingElse = () => {} doSomethingElse() return 'test' } doSomething()
La portée d'une fonction imbriquée est limitée par une fonction extérieure à elle; elle ne peut pas être appelée de l'extérieur.
Méthodes d'objet
Lorsque des fonctions sont utilisées comme propriétés d'objets, ces fonctions sont appelées méthodes d'objet.
const car = { brand: 'Ford', model: 'Fiesta', start: function() { console.log(`Started`) } } car.start()
Ce mot-clé
Si nous comparons la flèche et les fonctions ordinaires utilisées comme méthodes d'objets, nous pouvons trouver leur différence importante, qui consiste dans la signification du mot
this
clé
this
. Prenons un exemple.
const car = { brand: 'Ford', model: 'Fiesta', start: function() { console.log(`Started ${this.brand} ${this.model}`) }, stop: () => { console.log(`Stopped ${this.brand} ${this.model}`) } } car.start()
Comme vous pouvez le voir, appeler la méthode
start()
conduit au résultat attendu, mais la méthode
stop()
ne fonctionne évidemment pas correctement.
Cela est dû au fait que le mot-clé this se comporte différemment lorsqu'il est utilisé dans les fonctions flèche et ordinaires. A savoir, le
this
dans la fonction flèche contient un lien vers le contexte qui inclut la fonction. Dans ce cas, lorsqu'il s'agit du navigateur, ce contexte est l'objet
window
.
Voici à quoi ressemble l'exécution d'un tel code dans la console du navigateur.
const test = { fn: function() { console.log(this) }, arrFn: () => { console.log(this) } } test.fn() test.arrFn()
Ce mot-clé figure dans les fonctions conventionnelles et fléchéesComme vous pouvez le voir, l'appeler dans une fonction régulière signifie appeler l'objet, et
this
dans la fonction flèche pointe vers la
window
.
Tout cela signifie que les fonctions fléchées ne conviennent pas au rôle des méthodes objet et constructeur (si vous essayez d'utiliser la fonction flèche comme constructeur, une
TypeError
sera
TypeError
).
Expressions fonctionnelles appelées immédiatement
L'expression de fonction immédiatement invoquée (IIFE) est une fonction qui est appelée automatiquement immédiatement après sa déclaration.
;(function () { console.log('executed') })()
Le point-virgule avant IIFE est facultatif, mais son utilisation vous permet de vous assurer contre les erreurs associées au placement automatique des points-virgules.
Dans l'exemple ci-dessus, le mot
executed
ira à la console, après quoi l'IFEF quittera. L'IFEF, tout comme les autres fonctions, peut restituer les résultats de son travail.
const something = (function () { return 'IIFE' })() console.log(something)
Après avoir exécuté cet exemple simple, la console obtiendra la ligne
IIFE
, qui s'est avérée être
something
après avoir exécuté l'expression de fonction immédiatement appelée. Il peut sembler que cette conception ne présente aucun avantage particulier. Cependant, si dans l'IFEF certains calculs complexes sont effectués qui ne doivent être effectués qu'une seule fois, après quoi les mécanismes correspondants ne sont pas nécessaires - l'utilité de l'IFEF est évidente. A savoir, avec cette approche, après l'exécution de IIFE, seul le résultat renvoyé par la fonction sera disponible dans le programme. De plus, nous pouvons rappeler que les fonctions peuvent renvoyer d'autres fonctions et objets. Nous parlons de fermetures, nous en parlerons ci-dessous.
Mise à niveau des fonctionnalités
Avant que le code JavaScript ne soit exécuté, il est réorganisé. Nous avons déjà parlé du mécanisme de hissage des variables déclarées à l'aide du mot-clé
var
. Un mécanisme similaire fonctionne avec des fonctions. À savoir, nous parlons du fait que les déclarations de fonctions au cours du traitement du code avant son exécution sont déplacées vers la partie supérieure de leur portée. Par conséquent, par exemple, il s'avère que vous pouvez appeler la fonction avant qu'elle ne soit déclarée.
doSomething()
Si vous déplacez un appel de fonction pour qu'il passe après sa déclaration, rien ne changera.
Si, dans une situation similaire, une expression fonctionnelle est utilisée, un code similaire générera une erreur.
doSomething()
Dans ce cas, il s'avère que bien que la déclaration de la variable
doSomething
monte en haut de l'étendue, cela ne s'applique pas à l'opération d'affectation.
Si, au lieu de
var
, vous utilisez les mots clés
let
ou
const
dans une situation similaire, ce code ne fonctionnera pas non plus, cependant, le système affichera un message d'erreur différent (
ReferenceError
plutôt que
TypeError
), car lors de l'utilisation de
let
et
const
, les déclarations variables et constantes ne sont pas déclenchées.
Fonctions fléchées
Nous allons maintenant parler davantage des fonctions fléchées que nous avons déjà rencontrées. Ils peuvent être considérés comme l'une des innovations les plus importantes de la norme ES6, ils diffèrent des fonctions ordinaires non seulement par leur apparence, mais aussi par leur comportement. De nos jours, ils sont très largement utilisés. Il n'y a peut-être pas un seul projet moderne où ils ne seraient pas utilisés dans la grande majorité des cas. Nous pouvons dire que leur apparence a changé à jamais l'apparence du code JS et les caractéristiques de son travail.
D'un point de vue purement externe, la syntaxe de déclaration des fonctions fléchées est plus compacte que la syntaxe des fonctions ordinaires. Voici la déclaration d'une fonction régulière.
const myFunction = function () {
Voici l'annonce de la fonction flèche, qui, en général, si vous ne tenez pas compte des caractéristiques des fonctions flèche, est similaire à la précédente.
const myFunction = () => {
Si le corps d'une fonction flèche ne contient qu'une seule commande, dont le résultat renvoie cette fonction, elle peut être écrite sans accolades et sans le mot clé
return
. Par exemple, une telle fonction renvoie la somme des arguments qui lui sont passés.
const myFunction = (a,b) => a + b console.log(myFunction(1,2))
Comme vous pouvez le voir, les paramètres des fonctions fléchées, comme dans le cas des fonctions ordinaires, sont décrits entre parenthèses. De plus, si une telle fonction ne prend qu'un seul paramètre, elle peut être spécifiée sans parenthèses. Par exemple, voici une fonction qui renvoie le résultat de la division du nombre qui lui est passé par 2.
const myFunction = a => a / 2 console.log(myFunction(8))
En conséquence, il s'avère que les fonctions fléchées sont très pratiques à utiliser dans les situations où de petites fonctions sont nécessaires.
▍ Retour implicite des résultats de la fonction
Nous avons déjà abordé cette caractéristique des fonctions fléchées, mais elle est si importante qu'elle devrait être discutée plus en détail. Nous parlons du fait que les fonctions flèches sur une seule ligne supportent le retour implicite des résultats de leur travail. Un exemple de retour d'une valeur primitive d'une fonction de flèche sur une seule ligne que nous avons déjà vu. Et si une telle fonction devait retourner un objet? Dans ce cas, les accolades du littéral objet peuvent confondre le système, donc les parenthèses sont utilisées dans le corps de la fonction.
const myFunction = () => ({value: 'test'}) const obj = myFunction() console.log(obj.value)
▍ Mot-clé et fonctions fléchées
Ci-dessus, lorsque nous avons examiné les fonctionnalités du
this
, nous avons comparé les fonctions régulières et les flèches. Cette section est destinée à attirer votre attention sur l'importance de leurs différences. Le
this
, en lui-même, peut entraîner certaines difficultés, car il dépend du contexte de l'exécution du code et de l'activation ou non du mode strict.
Comme nous l'avons vu, lorsque vous utilisez le
this
dans une méthode d'un objet représenté par une fonction régulière,
this
pointe vers l'objet auquel appartient la méthode. Dans ce cas, nous parlons de lier le mot
this
clé
this
à une valeur qui représente le contexte de la fonction. En particulier, si une fonction est appelée en tant que méthode objet, le mot-clé this est lié à cet objet.
Dans le cas des fonctions fléchées, il s'avère que la liaison n'est pas effectuée en elles; elles utilisent le
this
de leur portée. Par conséquent, il n'est pas recommandé de les utiliser comme méthodes d'objet.
Le même problème se produit lors de l'utilisation de fonctions comme gestionnaires d'événements pour les éléments DOM. Par exemple, le
button
élément HTML
button
utilisé pour décrire les boutons. L'événement
click
est
click
lorsqu'un utilisateur clique sur un bouton. Pour répondre à cet événement dans le code, vous devez d'abord obtenir un lien vers l'élément correspondant, puis lui affecter un gestionnaire d'événements
click
tant que fonction. En tant que tel gestionnaire, vous pouvez utiliser à la fois la fonction régulière et la fonction flèche. Mais, si dans le gestionnaire d'événements vous devez accéder à l'élément pour lequel il est appelé (c'est-à-dire à
this
), la fonction de flèche ne fonctionnera pas ici, car la valeur
this
disponible en elle pointe vers l'objet
window
. Afin de tester cela en pratique, créez une page HTML dont le code est illustré ci-dessous et cliquez sur les boutons.
<!DOCTYPE html> <html> <body> <button id="fn">Function</button> <button id="arrowFn">Arrow function</button> <script> const f = document.getElementById("fn") f.addEventListener('click', function () { alert(this === f) }) const af = document.getElementById("arrowFn") af.addEventListener('click', () => { alert(this === window) }) </script> </body> </html>
Dans ce cas, lorsque vous cliquez sur ces boutons, des fenêtres contenant
true
apparaîtront. Cependant, dans le gestionnaire d'événements
click
du bouton avec l'identifiant
fn
, l'égalité de
this
le bouton est vérifiée, et dans le bouton avec l'identifiant
arrowFn
l'égalité de
this
et l'objet de la
window
vérifiés.
Par conséquent, si vous devez appeler
this
dans le gestionnaire d'événements de l'élément HTML, la fonction flèche ne fonctionnera pas pour la conception d'un tel gestionnaire.
Court-circuits
Les fermetures sont un concept important en JavaScript. En fait, si vous avez écrit des fonctions JS, vous avez également utilisé des fermetures. Les fermetures sont utilisées dans certains modèles de conception - dans le cas où vous devez organiser un contrôle strict de l'accès à certaines données ou fonctions.
Lorsqu'une fonction est appelée, elle a accès à tout ce qui est à la portée de l'extérieur. Mais il n'y a pas d'accès à ce qui est déclaré à l'intérieur de la fonction. Autrement dit, si une variable (ou une autre fonction) a été déclarée dans une fonction, elles sont inaccessibles au code externe, soit pendant l'exécution de la fonction, soit après l'achèvement de son travail. Cependant, si une autre fonction est renvoyée par la fonction, cette nouvelle fonction aura accès à tout ce qui a été déclaré dans la fonction d'origine. Dans ce cas, tout cela sera caché au code externe dans la fermeture.
Prenons un exemple. Voici une fonction qui prend le nom du chien, puis l'affiche dans la console.
const bark = dog => { const say = `${dog} barked!` ;(() => console.log(say))() } bark(`Roger`)
La valeur renvoyée par cette fonction ne nous intéresse pas encore, le texte est affiché dans la console en utilisant IIFE, qui dans ce cas ne joue pas de rôle particulier, cependant, cela nous aidera à voir la connexion entre cette fonction et sa variante, dans laquelle, au lieu d'appeler une fonction qui affiche texte à la console, nous retournerons cette fonction à partir de la fonction réécrite
bark()
.
const prepareBark = dog => { const say = `${dog} barked!` return () => console.log(say) } const bark = prepareBark(`Roger`) bark()
Le résultat du code dans deux cas est le même. Mais dans le deuxième cas, ce qui a été transféré à la fonction d'origine lors de son appel (le nom du chien,
Roger
) est stocké dans la fermeture, après quoi il est utilisé par une autre fonction revenue de l'original.
Faisons une autre expérience - créez, en utilisant la fonction d'origine, deux nouvelles pour différents chiens.
const prepareBark = dog => { const say = `${dog} barked!` return () => { console.log(say) } } const rogerBark = prepareBark(`Roger`) const sydBark = prepareBark(`Syd`) rogerBark() sydBark()
Ce code affichera les éléments suivants.
Roger barked! Syd barked!
Il s'avère que la valeur de la constante constante est liée à la fonction renvoyée par la fonction
prepareBark()
.
Notez que
say
, lorsque vous appelez à nouveau
prepareBark()
, il obtient une nouvelle valeur, tandis que la valeur enregistrée dans
say
première fois que
prepareBark()
appelée ne change pas. Le fait est qu'à chaque appel à cette fonction, une nouvelle fermeture est créée.
Résumé
Aujourd'hui, nous avons parlé des fonctions ordinaires et des flèches, des caractéristiques de leur déclaration et de leur utilisation, du comportement de
this
mot clé dans différentes situations et des fermetures. La prochaine fois, nous discuterons des tableaux et des boucles.
Chers lecteurs! Que pensez-vous des fonctions flèches en JavaScript?
