Fonctions fléchées VS classiques

 

 

Introduction

Chez ELEPHANT nos collaborateurs travaillent sur des projets en électronique, embarqué, software ou web chez nos clients nantais. 

Aujourd'hui retrouvez un article rédigé par Antoine développeur fullstack, qui nous explique les fonctions classiques et les fonctions fléchées dans le code javascript.

Allez c'est parti !

Vous avez sûrement vu passer dans du code javascript des fonctions classiques et des fonctions appelées “fonctions fléchées”. Ces fonctions fléchées sont apparues dans la version ECMAScript 2015 de Javascript. Mais quelle est vraiment la différence entre ces deux types de fonctions, dans quels cas les utiliser?

Dans cet article nous considérons que le mode strict de javascript n’est pas activé.

 

Fonctions classiques

 

Les fonctions “classiques” sont celles déclarées à l’aide du mot clé function apparu dès la première version de javascript en 1997. Il est possible d’utiliser ce mot clé de deux façons pour définir des fonctions:

// En utilisant les déclarations de fonctions
function helloWorld2() {
   console.log('Hello world');
}

// ou les expressions de fonctions
const helloWorld3 = function() {
    console.log('Hello world');
}


Dans le bloc de code ci-dessous, même si l’appel est fait avant la définition de la fonction, le programme se déroule sans erreur car les déclarations de fonctions sont chargées avant et donc la fonction helloWorld() est bien connue.Ces deux définitions de fonction seront appelées de la même façon à un détail près: les déclarations de fonctions sont chargées avant que le code soit exécuté.

 

helloWorld();

function helloWorld() {
    console.log('Hello world !');
}

 

Cependant dans la même situation avec les expressions de fonctions une erreur sera jetée :

ReferenceError: Cannot access 'helloNewWorld' before initialization

La fonction helloNewWorld() n’est pas encore connue car les expressions de fonctions sont chargées à l'exécution.

 

helloNewWorld(); // <-- ERREUR
const helloNewWorld = function () {
    console.log('Hello world !');
}

 

Voyons maintenant une limite à ces fonctions dites classiques (on se basera sur les déclarations et non les expressions de fonctions). On considère le code suivant:

const Avion = {
    altitude: 32000,
    atterrir: function() {
        setTimeout(function () {
            this.altitude = 0;
            console.log('Atterrissage réussi.');
        }, 1000);
    }
}

Avion.atterrir();
setTimeout(() => {
    console.log(`Altitude: ${Avion.altitude}`);
}, 1300);

 

On crée donc un objet Avion qui est à 32000 pieds, en appelant la fonction atterrir() on met l’altitude à zéro après 1 seconde et on affiche un message. On vérifie ensuite si l’avion est bien au sol avec une altitude à 0 en affichant sa valeur.

 

Lorsqu’on lance ce code voilà ce qu’affiche la console:

Atterrissage réussi.
Altitude: 32000

Arf, à 32000 pieds ouvrir les portes pour faire descendre les passagers c’est pas la meilleur idée.

 

Voyons ce qu’il c’est passé: ajoutons un log.

const Avion = {
    altitude: 32000,
    atterrir: function() {
        setTimeout(function () {
            console.log(this);
            this.altitude = 0;
            console.log('Atterrissage réussi.');
        }, 1000);
    }
}

 

 La console affiche:

Timeout {
  _idleTimeout: 1000,
  _idlePrev: null,
  _idleNext: null,
  _idleStart: 103,
  _onTimeout: [Function (anonymous)],
  _timerArgs: undefined,
  _repeat: null,
  _destroyed: false,
  [Symbol(refed)]: true,
  [Symbol(kHasPrimitive)]: false,
  [Symbol(asyncId)]: 2,
  [Symbol(triggerId)]: 1
}

 

Ce qui signifie que le this dans le setTimeout est un objet de type Timeout mais pas notre objet attendu Avion.

 

Ce fonctionnement est un des points à bien comprendre. Dans les fonctions “classique” le mot clé this dépends de la façon dont elle est appelée:

  • Si c’est un appel simple (comme notre première fonction helloWorld()), this pointera sur global ou window en fonction de si on se trouve dans un navigateur ou sur node.
  • Si la fonction est une méthode d’un objet comme dans notre objet Avion celui-ci pointera sur l’objet en question.
  • Si la fonction est appelée avec apply() ou call(), this sera égale au premier paramètre passé à la fonction.
  • Si la fonction est appelée en callback, elle utilisera le contexte présent lors de l'exécution de ce callback.

 

 

 

 

Dans notre exemple plus haut:

setTimeout(function () {
    this.altitude = 0;
    console.log('Atterrissage réussi.');
}, 1000);

La fonction n’est pas appelée directement par notre fichier app.js mais elle le sera dans l’objet Timeout ce qui explique la valeur de this.

 

Une des façons de pallier à ce problème de this est d’utiliser les fonctions fléchées.

 

Fonctions fléchées

 

Les fonctions fléchées permettent dans un premier temps de simplifier l’écriture du code dans certaines situations. Par exemple si l’on souhaite récupérer un tableau contenant le nombre de lettres de chaque mot présent dans un tableau donné, on peut faire avec les fonctions classiques:

 

const avions = [
    'Airbus',
    'Embraer',
    'Cessna'
  ];
 
console.log(avions.map(function(avion) {
    return avion.length;
}));

 

Dans cette situation, bien que le code fonctionne, il est plus approprié d’utiliser une première particularité des fonctions fléchées: le retour implicite. Le code précédent peut donc être résumé de la façon suivante :

const avions = [
  'Airbus',
  'Embraer',
  'Cessna'
];

console.log(avions.map(avion => avion.length));

 

Le mot clé return n’est donc pas nécessaire ici. Il est important de savoir que pour utiliser le retour implicite avec des objets littéraux il est nécessaire de les entourer de parenthèses.

 

Cet usage est plutôt intéressant dans ce cas, mais comment ces fonctions fléchées peuvent-elles nous aider à résoudre notre problème de this?

 

Une autre propriété de ce type de fonctions se situe dans la gestion du this qui n’est pas gérée de la même façon que les fonctions classiques. En effet, les fonctions fléchées

ne créent pas de nouveau contexte et utilisent le this du contexte courant.

 

Ce qui signifie que dans notre exemple plus haut si on remplace la fonction classique par une fonction fléchée dans le callback de setTimeout, le this utilisé sera bien l’objet Avion :

 

const Avion = {
    altitude: 32000,
    atterrir: function() {
        setTimeout(() => {

            console.log(this);
            this.altitude = 0;
            console.log('Atterrissage réussi.');
        }, 1000);
    }
}

 

Ici tout fonctionnera correctement car à l’intérieur du setTimeout, le this sera celui du contexte courant c'est-à-dire l’objet Avion. Notre console nous affiche :

{ altitude: 32000, atterrir: [Function: atterrir] }
Atterrissage réussi.

Altitude: 0

 

Le this.altitude fait bien référence à la propriété de notre objet Avion et sa valeur est donc correctement mise à zéro.

 

Conclusion

 

Nous avons vu ici l 'usage des deux types de fonctions ainsi que leurs utilités dans certaines situations. Quand vous jetez un coup d'œil à la documentation MDN sur les fonctions fléchées vous verrez qu’ils conseillent de ne pas utiliser ces fonctions pour déclarer des méthodes. En effet les fonctions fléchées sont plutôt utilisées en tant que fonction anonyme notamment dans le cas de callback ou de manipulation de tableau par exemple comme vu plus haut.

 

Chaque type de fonction a donc des avantages et inconvénients qu’il est important de connaître. Si vous souhaitez en savoir plus sur le sujet, je vous invite à aller consulter la documentation des fonctions fléchées et fonctions classiques sur MDN. J’en n’ai volontairement pas parlé ici mais il est également possible de contrer le this capricieux des fonctions classique en appelant les fonctions via apply ou call.

 

Sources

https://developer.mozilla.org/fr/docs/Web/JavaScript/Guide/Functions

https://medium.com/@mandeep1012/function-declarations-vs-function-expressions-b43646042052

https://www.youtube.com/watch?v=IudrkWwOw8Y

https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Operators/this

https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Functions/Arrow_functions

 

Et pour découvrir d'autres articles techniques c'est par ici : 

Les object calisthenics : https://www.elephant-technologies.fr/actualite/43

Les tests dans une application : https://www.elephant-technologies.fr/actualite/42