Guide JavaScript Partie 4: Fonctionnalités

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 = () => { // - } const doSomethingElse = foo => { // - } const doSomethingElseAgain = (foo, bar) => { // - } 

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') => { // - } const args = [2, 'ho!'] doSomething(...args) 

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' }) => { // - console.log(foo) // 2 console.log(bar) // 'ho!' } const args = { foo: 2, bar: 'ho!' } doSomething(args) 

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') => { // - } console.log(doSomething()) 

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() // result === 'test' 

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) //Roger 6 

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) //Roger 6 

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() //Started Ford Fiesta car.stop() //Stopped undefined undefined 

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ées

Comme 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() //did something function doSomething() { console.log('did something') } 

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() //TypeError var doSomething = function () { console.log('did something') } 

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)) //3 

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)) //4 

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) //test 

▍ 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`) // Roger barked! 

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() // Roger barked! 

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?

Source: https://habr.com/ru/post/fr430382/


All Articles