EcmaScript 8e édition

Table Des Matières

Bienvenu EcmaScript 8


EcmaScript 8 ou EcmaScript 2017 défini par la norme Ecma, est la 8ème édition de la spécification ECMAScript. Depuis la publication de la première édition en 1997, ECMAScript est devenu l’une des langues de programmation généralisées les plus répandues dans le monde.Généralement conçu pour intégrer les navigateurs Web, l’ECMAScript a également été largement adopté pour intégrer les serveur et les applications.

ES8 a été officiellement publié fin juin par TC39. Il semble qu’on a trop parlé à son propos l’année dernière,et ce n’est pas pour rien. Tout au long de l’année du développement de cette norme, des centaines de requêtes et de problèmes de tirage ont été déposées représentant des milliers de corrections de bogues, des corrections rédactionnelles et d’autres améliorations. En outre, de nombreux outils logiciels ont été développés pour aider dans cet effort, y compris Ecmarkup, Ecmarkdown et Grammarkdown. Cette spécification comprend également la prise en charge d’un nouvel opérateur d’exponentiation et ajoute une nouvelle méthode à Array.prototype appelée  includes. Des dizaines d’individus représentant de nombreuses organisations ont apporté des contributions très importantes au sein d’Ecma TC39 au développement de cette édition et aux éditions précédentes.

En outre, une communauté dynamique a vue le jours et dont le but principal est d’appuyer  les efforts de TC39 sur ECMAScript. Cette communauté a examiné de nombreux projets, a déposé des milliers de rapports de bogues,a effectué des expériences de mise en œuvre, des ensembles de tests proposés et a sensibilisé et bien informé  la communauté des développeurs mondiale (world-wide developer community) sur ECMAScript. Malheureusement, il est impossible d’identifier et de reconnaître toutes les personnes et toutes les organisation ayant contribué à cet effort.

Quoi de neuf Chez ES8?


ECMAScript 8 fait référence aux fonctionnalités ajoutées au standard ECMA-262 depuis la version ECMAScript 7 (ES2016). Depuis ECMAScript 6, des versions du standard sont publiée chaque année. Cette année, la spécification ES2017 vient de voir le jours fin juin dernier. Avec ses nouvelles fonctionnalités implémentées, cette éditon représente l’évolution du standard ECMA-262.

String Padding


String.prototype.padStart()

Syntaxe
str.padStart(longueurCible [, chaîneComplémentaire])

La méthode padStart() consiste à remplir la chaîne courante avec une certaine séquence de caractères  afin d’obtenir une chaîne de longueur fixée. Pour avoir cette longueur, la chaîne complémentaire peut être répétée. La chaîne courante est complétée depuis le début.

Paramètres: longueurCible –La longueur de la chaîne qu’on souhaite obtenir. Si la longueur indiquée est inférieure à celle de la chaîne courante, cette dernière est renvoyée telle quelle.
chaîneComplémentaire (Facultatif):  La chaîne de caractères avec laquelle on veut compléter la chaîne courante. Si cette chaîne est trop longue, on prendra uniquement le début (la partie la plus à gauche). La valeur par défaut de ce paramètre est l’espace  »  » (U+0020). Si cette chaîne est trop courte, elle sera répétée.
Valeur de retour: Une chaîne de caractères (String) dont la longueur est celle indiquée, complétée avec la chaîne fournie au début de la chaîne courante.

Exemple

'abc'.padStart(10);         // "        abc"
'abc'.padStart(10, "toto"); // "totototabc"
'abc'.padStart(6,"123465"); // "123abc"
'abc'.padStart(8, "0");     // "00000abc"
'abc'.padStart(1);          // "ab

Notez bien que cette méthode a été ajoutée à partir d’ECMAScript 2015 et ne peut être disponible pour l’ensemble des environnements JavaScript. Il est cependant possible d’ajouter une prothèse pour cette méthode :

// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
// https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/String/repeat
if (!String.prototype.padStart) {
  String.prototype.padStart = function (count, str) {
    return (str || ' ').repeat(count - this.length).substr(0,count) + this;
  };
}

String.prototype.padEnd()

Syntaxe
str.padEnd(longueurCible [, chaîneComplémentaire])

La méthode padEnd() permet de compléter la chaîne courante avec une chaîne de caractères données afin d’obtenir une chaîne de longueur fixée. Pour atteindre cette longueur, la chaîne complémentaire peut être répétée. La chaîne courante est complétée depuis la fin.

Paramètres: longueurCible –La longueur de la chaîne qu’on souhaite obtenir. Si la longueur indiquée est inférieure à celle de la chaîne courante, cette dernière est renvoyée telle quelle.
chaîneComplémentaire (Facultatif) –La chaîne de caractères avec laquelle on veut compléter la chaîne courante. Si cette chaîne est trop longue, on prendra uniquement le début (la partie la plus à gauche). La valeur par défaut de ce paramètre est l’espace  »  » (U+0020). Si cette chaîne est trop courte, elle sera répétée.
Valeur de retour: Une chaîne de caractères (String) dont la longueur est celle indiquée, complétée avec la chaîne fournie.

Exemple

'abc'.padEnd(10);         // "abc       "
'abc'.padEnd(10, "toto"); // "abctototot"
'abc'.padEnd(6,"123456"); // "abc123"
'abc'.padEnd(1);          // "abc

Notez bien que cette méthode a été ajoutée à partir d’ECMAScript 2015 et peut ne pas être disponible pour l’ensemble des environnement. Il est toutefois possible de fournir une prothèse pour cette méthode :

// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
// https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/String/repeat
if (!String.prototype.padEnd) {
  String.prototype.padEnd = function (count, str) {
    return this + (str || ' ').repeat(count).substr(0,count);
  };
}

Object.values() & Object.entries()


Object.values()

Syntaxe:
Object.values(obj)

La méthode Object.values() renvoie un tableau contenant les valeurs des propriétés propres énumérables d’un objet dont l’ordre est le même que celui obtenu avec une boucle for…in (la boucle for-in est différente car elle parcourt également les propriétés héritées.

Paramètres: obj –L’objet dont on souhaite connaître les valeurs des propriétés propres énumérables.

Valeur de retour: Un tableau dont les éléments sont les valeurs des propriétés énumérables de l’objet passé en argument.

Exemples:

var obj = { toto: "truc", machin: 42 };
console.log(Object.values(obj)); // ['truc', 42]

// un objet semblable à un tableau
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.values(obj)); // ['a', 'b', 'c']

// un objet semblable à un tableau
// dont les clés sont ordonnées aléatoirement
var un_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.values(un_obj)); // ['b', 'c', 'a']

// getToto est une propriété qui
// n'est pas énumérable
var mon_obj = Object.create({}, { getToto: { value: function() { return this.toto; } } });
mon_obj.toto = "truc";
console.log(Object.values(mon_obj)); // ['truc']

// un argument de type primitif sera
// converti en un objet
console.log(Object.values("toto")); // ['t', 'o', 't', 'o']

Object.entries()

Syntaxe
Object.entries(obj)

La méthode Object.entries() renvoie un tableau des propriétés propres énumérables d’un objet sous la forme de paires [clé, valeur], dans le même ordre qu’une boucle for…in (la boucle for-in est différente car elle parcourt la chaîne des prototypes).

Le constructeur new Map() accepte un argument itérable pour décrire les entrées du tableau associatif. Grâce à Object.entries, il est possible de convertir simplement un objet Object en un objet Map :

var obj = { toto: "truc", machin: 42 }; 
var map = new Map(Object.entries(obj));
console.log(map); // Map { toto: "truc", machin: 42 }

Paramètres: obj –L’objet dont on souhaite connaître les propriétés propres énumérables sous la forme de paires [clé, valeur].

Valeur de retour: Un tableau qui contient les propriétés énumérables propres de l’objet sous la forme de paires [clé, valeur]

Exemples

var obj = { toto: "truc", machin: 42 };
console.log(Object.entries(obj)); // [ ['toto', 'truc'], ['machin', 42] ]

// Un objet semblable à un tableau
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]

// Un objet semblable à un tableau
// dont les clés sont aléatoirement ordonnées
var un_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(un_obj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]

// getToto est une propriété non énumérable
var mon_obj = Object.create({}, { getToto: { value: function() { return this.toto; } } });
mon_obj.toto = "truc";
console.log(Object.entries(mon_obj)); // [ ['toto', 'truc'] ]

// un argument de type primitif sera
// converti en un objet
console.log(Object.entries("toto")); // [ ['0', 't'], ['1', 'o'], ['2', 't'],  ['3', 'o'] ]

// parcourir les clés-valeurs
var autreObjet = {a:5, b:7, c:9};

for (var [cle, valeur] of Object.entries(autreObjet)){
  console.log(cle + ' ' + valeur);
}

// Ou encore, en utilisant les méthodes génériques
Object.entries(autreObjet).forEach(([clé, valeur]) => {
  console.log(clé + ' ' + valeur);
});

Object.getOwnPropertyDescriptors()


Syntaxe
Object.getOwnPropertyDescriptors(obj)

La méthode Object.getOwnPropertyDescriptors() renvoie l’ensemble des descripteurs des propriétés propres d’un objet donné. En effet cette méthode permet d’examiner de façon précise les différentes propriétés directement rattachées à un objet. Une propriété JavaScript se définit par un nom (une chaîne de caractères) et un descripteur. Vous pouvez trouver de plus amples informations sur les types de descripteurs et sur leurs attributs sur la page de Object.defineProperty().

Un descripteur de propriété est un enregistrement qui possède les attributs suivants :

value –La valeur associée à la propriété (uniquement pour les descripteurs de données).

writable — true si et seulement si la valeur associée à la propriété peut être changée (uniquement pour les descripteurs de données).

get –Une fonction qui est utilisée comme accesseur pour la propriété ou undefined s’il n’existe pas de tel accesseur (uniquement pour les descripteurs d’accesseur/mutateur).

set –Une fonction qui est utilisée comme mutateur pour la propriété ou undefined s’il n’existe pas de tel mutateur (uniquement pour les descripteurs d’accesseur/mutateur).

configurable –true si et seulement si le type de descripteur peut être changé et si la propriété peut être supprimée de l’objet correspondant.

enumerable — true si et seulement si cette propriété est listée lorsqu’on énumère les propriétés de l’objet correspondant.

Exemples

Créer un clone: La méthode Object.assign() ne copiera que les propriétés propres et énumérables d’un objet source vers un objet cible. On peut donc utiliser cette méthode avec Object.create() afin de réaliser une copie « plate » entre deux objets inconnus :

Object.create(
  Object.getPrototypeOf(obj), 
  Object.getOwnPropertyDescriptors(obj) 
);

Créer une sous-classe –Pour créer une sous-classe, généralement, on définit la sous-classe et on définit son prototype comme étant une instance de la classe parente. Enfin on définit les propriétés de cette nouvelle sous-classe.

function superclass() {};
superclass.prototype = {
  // on définit les méthodes et propriétés
  // de la classe parente
};

function subclass() {};
subclass.prototype = Object.create(
  superclass.prototype,
  Object.getOwnPropertyDescriptors({
  // on définit les méthodes et propriétés
  // de la sous-classe
}));

Async Function


Déclaration de fonction asynchrone

Syntaxe
async function name([param[, param[, … param]]]) {
instructions
}

La déclaration async function définit une fonction asynchrone qui renvoie un objet AsyncFunction. On peut également définir des fonctions asynchrones grâce au constructeur AsyncFunction et via une expression de fonction asynchrone.

Lorsqu’une fonction asynchrone est appelée, elle renvoie une promesse. Lorsque la fonction asynchrone renvoie une valeur, la promesse est résolue avec la valeur renvoyée. Lorsque la fonction asynchrone lève une exception, la promesse est rompue avec la valeur de l’exception.

Une fonction asynchrone peut contenir une expression await qui permet d’interrompre l’exécution de la fonction asynchrone en attendant la résolution d’une promesse passée à l’expression. L’exécution de la fonction asynchrone reprend lorsque la promesse est résolue.

L’objectif des fonctions asynchrones avec await est de simplifier le comportement des promesses lors d’opérations synchrones et d’opérer sur des groupes de promesses. Si les promesses sont en quelque sorte des callbacks organisés, async/await permet de combiner les générateurs et les promesses.

Paramètres: name — Le nom de la fonction.
param — Le nom d’un argument à passer à la fonction.
instructions –Les instructions qui composent le corps de la fonction.
Valeur de retour: Un objet AsyncFunction} qui représente une fonction asynchrone qui exécute le code contenu dans la fonction.

Exemples

Exemple simple

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function add1(x) {
  var a = resolveAfter2Seconds(20);
  var b = resolveAfter2Seconds(30);
  return x + await a + await b;
}

add1(10).then(v => {
  console.log(v);  // affiche 60 après 2 secondes.
});

async function add2(x) {
  var a = await resolveAfter2Seconds(20);
  var b = await resolveAfter2Seconds(30);
  return x + a + b;
}

add2(10).then(v => {
  console.log(v);  // affiche 60 après 4 secondes.
});

Réécrire une chaîne de promesses avec des fonctions asynchrones

Lorsqu’on utilise des API qui renvoient des promesses (cf. Promise), on finit par créer des  chaînes de promesses et on divise alors une fonction en de nombreux fragments. Prenons l’exemple suivant :

function getProcessedData(url) {
  return downloadData(url) // renvoie une promesse
    .catch(e => {
      return downloadFallbackData(url); // renvoie une promesse
    })
    .then(v => {
      return processDataInWorker(v); // renvoie une promesse
    });
}

 

On peut réécrire ce fragment de code en une seule fonction asynchrone :

async function getProcessedData(url) {
  let v;
  try {
    v = await downloadData(url); 
  } catch (e) {
    v = await downloadFallbackData(url);
  }
  return processDataInWorker(v);
}

Dans l’exemple précédent, on n’inclue pas d’instruction await dans l’instruction return car la valeur de retour d’une fonction asynchrone est implicitement passée dans la méthode Promise.resolve.

Notez bien qu‘à partir de Firefox 55, la syntaxe des expressions de fermeture n’est pas autorisée au sein des fonctions asynchrones et déclenchera une exception SyntaxError

Expression async function

Syntaxe
async function [name]([param1[, param2[, …, paramN]]]) {
instructions
}

Le mot-clé async function peut être utilisé pour définir une fonction asynchrone au sein d’une expression. Il est aussi possible de définir une fonction asynchrone en utilisant une instruction async function.

Une expression async function est très proche, et partage quasiment la même syntaxe avec une instruction async function. La différence principale entre une expression async function et une instruction async function est qu’on peut omettre le nom de la fonction dans les expressions async function. On peut donc utiliser une expression async function afin de créer une IIFE (pour Immediately Invoked Function Expression) qu’on appelle au moment de sa définition. Voir également le chapitre sur les fonctions pour plus d’informations.

Paramètres: name –Le nom de la fonction. Il est facultatif et s’il n’est pas utilisé, la fonction est anonyme. Le nom utilisé est uniquement local pour le corps de la fonction.
paramN –Le nom d’un argument à passer à la fonction.
instructions –Les instructions qui composent le corps de la fonction.

Exemple simple

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
};

(async function(x) { // fonction asynchrone immédiatement appelée
  var a = resolveAfter2Seconds(20);
  var b = resolveAfter2Seconds(30);
  return x + await a + await b;
})(10).then(v => {
  console.log(v);  // affiche 60 après 2 secondes.
});

var add = async function(x) {
  var a = await resolveAfter2Seconds(20);
  var b = await resolveAfter2Seconds(30);
  return x + a + b;
};

add(10).then(v => {
  console.log(v);  // affiche 60 après 4 secondes.
});

AsyncFunction

Syntaxe
new AsyncFunction ([arg1[, arg2[, …argN]],] functionBody)

Le constructeur AsyncFunction crée un nouvel objet pour une fonction asynchrone. En JavaScript, chaque fonction asynchrone est en fait un objet AsyncFunction.

Les objets des fonctions asynchrones créés avec le constructeur AsyncFunction sont analysés lorsque la fonction est créée. C’est moins efficace que de déclarer une fonction asynchrone avec une expression de fonction asynchrone et de l’appeler depuis le code car ces fonctions sont analysées avec le reste du code.

Tous les arguments passés à la fonction sont traités comme les noms des identifiants des paramètres de la fonction qui sera créée, dans l’ordre dans lequel ils sont passés.

Si on appelle AsyncFunction comme une fonction (c’est-à-dire sans new), cela aura le même effet que s’il est appelé comme un constructeur.

Attention!!  AsyncFunction n’est pas un objet global. On peut l’obtenir grâce au code suivant :

Object.getPrototypeOf(async function(){}).constructor

 

Notez bien que les fonctions asynchrones créées avec le constructeur AsyncFunction ne créent pas de fermetutres dans leurs contextes de création. Elles sont toujours créées dans la portée globale. Lorsqu’on les exécute, ellee ne pourront accéder qu’à leurs variables locales et aux variables globales, pas à celles qui appartiennent à la portée dans laquelle AsyncFunction a été appelé. On aurait donc un comportement différent si on appelait eval avec le code de l’expression de la fonction asynchrone.

Paramètres: arg1, arg2, … argN –Les noms des paramètres passés à la fonction. Chacun doit être une chaîne de caractères qui puisse être un identifiant JavaScript valide ou une liste de telles chaînes séparées par une virgule (ex. « x », « laValeur », ou « a,b »).
functionBody –Une chaîne de caractères qui contient les instructions JavaScript définissant la définition de la fonction.

Prototype de l’objet AsyncFunction
Propriétés:

AsyncFunction.constructor –La valeur initiale est AsyncFunction.
AsyncFunction.prototype[@@toStringTag] –Renvoie « AsyncFunction ».

Instances AsyncFunction
Les instances d’AsyncFunction héritent des méthodes et des propriétés de AsyncFunction.prototype. Comme avec les autres constructeurs, on peut changer l’objet prototype du constructeur afin de modifier l’ensemble des instances AsyncFunction.

Exemple: Créer une fonction asynchrone avec un constructeur AsyncFunction

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

var AsyncFunction = Object.getPrototypeOf(async function(){}).constructor
var a = new AsyncFunction("a",
                          "b",
                          "return await resolveAfter2Seconds(a) + await resolveAfter2Seconds(b);");
a(10, 20).then(v => {
  console.log(v); // affiche 30 après 4 secondes
});

await

Syntaxe
[rv] = await expression;

expression –Une promesse (Promise) ou toute autre valeur dont on souhaite attendre la résolution.
rv –La valeur de retour qui est celle de la promesse lorsqu’elle est résolue ou la valeur de l’expression lorsque celle-ci n’est pas une promesse.

L’opérateur await permet d’attendre la résolution d’une promesse (Promise). Il ne peut être utilisé qu’au sein d’une fonction asynchrone (définie avec l’instruction async function).

L’expression await interrompt l’exécution d’une fonction asynchrone et attend la résolution d’une promesse. Lorsque la promesse est résolue, la valeur est renvoyée et l’exécution de la fonction asynchrone reprend. Si la valeur de l’expression n’est pas une promesse, elle est convertie en une promesse résolue ayant cette valeur.
Si la promesse est rompue, l’expression await lève une exception avec la raison.

Exemples

Si on passe une promesse à une expression await, celle-ci attendra jusqu’à la résolution de la promesse et renverra la valeur de résolution.

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

async function f1() {
  var x = await resolveAfter2Seconds(10);
  console.log(x); // 10
}
f1();

Si la valeur n’est pas une promesse, elle est convertie en une promesse résolue :

async function f2() {
  var y = await 20;
  console.log(y); // 20
}
f2();

Si la promesse est rejetée, la raison est fournie avec l’exception.

async function f3() {
  try {
    var z = await Promise.reject(30);
  } catch (e) {
    console.log(e); // 30
  }
}
f3();

Les virgules en fin de ligne pour la liste des paramètres d’une fonction

C’est la capacité du compilateur -Ne pas soulever d’erreur- SyntaxError Lorsque nous ajoutons une virgule inutile à la fin de la liste:

function es8(var1, var2, var3,) {
  // ...
}

En tant que déclaration, cela peut être appliqué sur les appels de la fonction:

es8(10, 20, 30,);

Sources :

ECMAScript Next support in Mozilla

Ecma-International

Note: Dans le prochain article je vais évoquer les nouvelles fonctionnalités expérimentales, qui sont d’ores et déjà implémentées mais ne sont disponibles que pour Firefox Nightly et ne font pas encore partie d’un brouillon de la spécification ECMAScript.

LAISSER UN COMMENTAIRE

Please enter your comment!
Please enter your name here

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.