ES6 : les fonctions

image_pdfimage_print

Les fonctions sont les pièces maîtresses de JavaScript, à tel point qu’il existe même une programmation fonctionnelle.

Dans ce chapitre on va voir les nouveautés de ES6 pour les fonctions : noms, portée et fonctions fléchées.

Le nom des fonctions

Il est souvent utile de connaître le nom d’une fonction, ne serait-ce que lorsqu’on débogue du code. Avec ES5 il existe une propriété name qui contient ce nom (mais pas pris en charge par tous les moteurs) :

function toto() {function toto() {};
var titi = function() {};
console.log(toto.name);  // toto
console.log(titi.name);  // titi

Quel est le nom d’une fonction anonyme ?

Par principe elle n’en a justement aucun parce qu’on n’a jamais à l’appeler mais on peut l’affecter à une variable comme on l’a fait pour titi ci-dessus, dans ce cas elle porte le nom de la variable.

On peut se demander comment sont nommées les méthodes d’un objet. Voyons avec un exemple :

let objet = {
    methode1() {},
    methode2: function () {}
};
console.log(objet.methode1.name); // methode1
console.log(objet.methode2.name); // methode2

Très logiquement on se retrouve avec le nom des méthodes.

Mais il y a le cas particulier des accesseurs (getters) et mutateurs (setters) :

let objet = {
    get name() {},
    set name(value) {}
};
console.log((Object.getOwnPropertyDescriptor(objet, 'name').get).name); // "get name"
console.log((Object.getOwnPropertyDescriptor(objet, 'name').set).name); // "set name"

On voit que le nom commence avec set ou get.

Le nom d’un fonction a seulement un caractère informatif, il ne peut pas être utilisé dans le code. Sa principale utilité concerne le débogage.

Portée des fonctions

Avec ES5 en mode strict on ne peut pas placer une fonction dans un bloc :

{
    function f() {};
}

Par contre on peut avoir une expression de fonction :

{
    var f = function () {};
}

Avec ES6 on peut placer une fonction dans un bloc, elle est alors accessible uniquement dans ce bloc.

Et évidemment dans le cas d’une expression de fonction on va utiliser let pour limiter la portée au bloc.

Les fonctions fléchées

Abordons maintenant une des nouveautés les plus intéressantes de ES6 : les fonctions fléchées.

Comme leur nom l’indique on utilise une nouvelle syntaxe avec une flèche =>‌

On va voir qu’on a deux avantages par rapport aux expressions de fonctions classiques :

  • une syntaxe courte,
  • un this lexical.

La syntaxe

Mais commençons par voir la syntaxe d’une fonction fléchée :

let resultat = valeur => valeur;
console.log(resultat(2));  // 2

J’ai défini la fonction resultat, on lui transmet une valeur et elle se contente de retourner cette valeur.

Bon elle sert à rien ma fonction mais c’est juste pour voir la syntaxe. En version ES5 on aurait ceci :

var resultat = function resultat(valeur) {
  return valeur;
};

On va aller un peu plus loin en utilisant deux paramètres :

let resultat = (valeur1, valeur2) => valeur1 * valeur2;
console.log(resultat(2, 3));  // 6

Cette fois ma fonction est utile, elle retourne le produit des deux valeurs transmises. En version ES5 on aurait ceci :

var resultat = function resultat(valeur1, valeur2) {
  return valeur1 * valeur2;
};

On voit que la syntaxe est bien plus concise et claire avec une fonction fléchée.

Si on a un peu plus de traitement à effectuer alors on utilise des accolades et return :

let resultat = (valeur1, valeur2) => {
  if(valeur1 > valeur2) {
    return valeur1;
  } else {
   	return valeur2; 
  }
}
console.log(resultat(2, 3));  // 3

La valeur de this

Avec ES5 la valeur de this est déterminée de façon dynamique et dépend du contexte (constructeur, fonction, méthode…), ce qui n’est pas sans surprises et difficultés.

Avec ES6 la valeur de this est déterminée de façon lexicale, autrement dit elle est déterminée par le contexte englobant.

Bon, si ce n’est pas très clair on va prendre des exemples !

Je veux créer un indexeur avec le choix du numéro de départ, voici le code que j’essaie :

function Numerote(depart) {
    this.depart = depart;
}
Numerote.prototype.numeroteTableau = function (tableau) { 
   	return tableau.map(function (x) {
        return x + this.depart++;
    });
};

var num = new Numerote(5);
console.log(num.numeroteTableau(['a', 'b', 'c']));// true

Mais mon code ne fonctionne pas parce que le this à l’intérieur de la fonction masque le this englobant.

Quand on tombe sur ce genre de situation on mémorise le this juste avant la fonction avec une autre variable :

function Numerote(depart) {
    this.depart = depart;
}
Numerote.prototype.numeroteTableau = function (tableau) { 
  	let that = this;
   	return tableau.map(function (x) {
        return x + that.depart++;
    });
};

var num = new Numerote(5);
console.log(num.numeroteTableau(['a', 'b', 'c']));  // ["a5","b6","c7"]

C’est efficace même si ce n’est pas vraiment élégant…

Une autre solution (qui fonctionne avec ECAMScript 5.1) consiste à lier this à la fonction :

return tableau.map(function (x) {
    return x + this.depart++;
}.bind(this));

Avec ES6 il suffit d’utiliser une méthode fléchée :

Numerote.prototype.numeroteTableau = function (tableau) { 
   	return tableau.map((x) => {
        return x + this.depart++;
    });
};

this a la référence correcte et tout fonctionne !

En résumé

  • Avec ES6 toutes les fonctions sont automatiquement nommées pour faciliter le débogage.
  • Avec ES6 une fonction dans un bloc a comme portée ce bloc.
  • Les fonctions fléchées simplifient la syntaxe et permettent d’avoir un this lexical.

Laisser un commentaire