Aller au contenu principal

ECMAScript (Cheat Sheet)

Introduction

Javascript est un langage en évolution (comme tous les langages) qui a donc connu des améliorations au fur et à mesure des années. Bien qu’au début les nouvelles éditions n’étaient pas régulières, depuis l’année 2015 (sortie de ES6 qui changea profondément le langage) les mises à jour sont annuelles. Cependant depuis l’ES6, à cause de cette nouvelle fréquence de sortie des éditions de Javascript, les changements sont mineurs Dans ce laboratoire, nous verrons les dernières nouveautés qui vous seront utiles lors de développement d’applications utilisant le JavaScript.

Style de programmation

JavaScript utilise deux styles de programmation : l’impératif et le déclaratif. L’impératif exprime le « comment » tandis que le déclaratif exprime le « quoi ». Prenons un exemple :

function double(array){
const rep = [];
for(let i = 0; i < array.length; i++){
rep.push(array[i]*2);
}
return rep;
}

Cette fonction est du style impératif, car on indique à la fonction « comment » faire. Tandis que cette fonction est du genre déclaratif :

function double (array){
return array.map(function(elem){
return elem * 2
});
}

La fonction map est une fonction qui applique une autre fonction sur chaque élément d’un tableau. Elle a besoin qu’on lui indique « quoi » faire pour chaque élément du tableau. Les deux approches sont complémentaires. En effet, dans le deuxième exemple, la fonction anonyme est du style impératif. Cette distinction est assez importante, car l’utilisation du style déclaratif est de plus en plus importante de nos jours. Il existe un nombre croissant de fonctions qui prennent comme argument d’autres fonctions qui indiquent le comportement à adopter (find(), map(), reduce(), …). Cependant, le déclaratif dans JavaScript n’est pas nouveau (voir les listerners) mais son utilisation est de plus en plus courante.

Déclaration de variable

Au début de JavaScript, pour déclarer une variable il fallait utiliser le mot clé « var ». Aujourd’hui, on utilise les mots clés « let » et « const ». Le premier se comporte de la même façon que « var » tandis que le second empêche toute modification de la valeur stockée.

const constante = 1;
constante = 2; //erreur

let variable = 1;
variable = 2; //fonctionne

Cependant, attention aux objets et tableaux ! La variable est un « pointeur » vers le tableau ou l’objet et non le contenu de ces derniers.

const obj1 = {a: 1};
obj1.a = 2; // fonctionne

const tab = [1];
tab[0] = 2; // fonctionne

Classe

Javascript permet de créer plus facilement des classes qu’avant. En effet, précédemment, les classes étaient des fonctions et on modifiait leur prototype. Aujourd’hui, JavaScript se rapproche beaucoup plus des autres langages de programmation avec des mots clés pour la déclaration des classes et l’héritage. Il est possible de facilement déclarer des méthodes statiques et non-statiques. Par exemple (source : https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Classes) :

class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}

static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);

console.log(Point.distance(p1, p2));

class Animal {
constructor(nom) {
this.nom = nom;
}

parle() {
console.log(`${this.nom} fait du bruit.`);
}
}

class Chien extends Animal {
constructor(nom) {
super(nom); // appelle le constructeur parent avec le paramètre
}
parle() {
console.log(`${this.nom} aboie.`);
}
}

Il est également possible de déclarer des attributs et méthodes privées en les précédant d’un « # » (depuis l’ECMAScript 2022). Voici un exemple :

class ClassWithPrivateField {
#privateField;
}

class ClassWithPrivateMethod {
#privateMethod() {
return 'hello world';
}
}

class ClassWithPrivateStaticField {
static #PRIVATE_STATIC_FIELD;
}

class ClassWithPrivateStaticMethod {
static #privateStaticMethod() {
return 'hello world';
}
}

Source : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields

Chaîne de caractères

Il existe deux symboles pour délimiter les chaînes de caractères : « " » et « ‘ ». Maintenant, il existe une troisième façon de déclarer des chaînes de caractères avec les « ` » (on les appelle des littéraux de gabarits ». Cela permet d’évaluer des valeurs dans la chaîne de caractères mais également de mettre cette chaîne sur plusieurs lignes sans utiliser le symbole « + » pour concaténer les lignes. Exemple :

const nom = "John";
const string = `Bonjour Mr ${nom}`;
const multipleLignes = `Bonjour
Je suis sur plusieurs lignes`;

Fonction fléchée

Les fonctions fléchées sont des fonctions délimitées par « (…) » pour les arguments, « {…} » pour le corps de la fonction et entre les deux il y a un « => » . Par exemple :

const maFonctionFlechee = (argument) => {
console.log(argument);
}

Il est également possible d’omettre les « » dans le cas d’une fonction écrite en une ligne avec une seule instruction :

const enUneLigne = (argument) => console.log(argument);

Au premier coup d’œil, on pourrait penser que les fonctions fléchées sont équivalentes à des fonctions anonymes. Cependant, c’est est faux ! En effet, les fonctions anonymes créent un nouveau contexte (« this ») au sein de la fonction tandis que la fonction fléchée reprend le « this » actuel. Par exemple :

const exemple = {

test: "test",
anonyme: function(){
console.log(this.test);
},
fleeche: () => {
console.log(this.test);
}

};

exemple.anonyme(); //"test"
exemple.fleeche(); // undefined

Fonction de haut niveau

Une fonction de haut niveau est une fonction qui a au moins une de ces deux caractéristiques :

  • Elle prend comme arguments une ou plusieurs fonctions
  • Elle renvoie une fonction comme valeur de retour

Exemple :

const monTableau = [1, 2, 3];

monTableau.map((chiffre) => return chiffre * 2); //return optionnel car une seule instruction


const triple = (chiffre) => chiffre * 3;

const maHOFCustom = (tableau, fonction) => {
return tableau.map(fonction);
}

maHOFCustom(monTableau, triple) // [3, 6, 9];

const renvoieUneFonction = (option) => {
if(option == "double"){
return double;
} else {
return triple;
}
}

const fonctionRenvoyee = renvoieUneFonction("double");

monTableau.map(fonctionRenvoyee) ; // [2, 4, 6]

Paramètre par défaut

Il est possible de définir une valeur par défaut pour un argument. Ainsi, si on ne le précise pas dans l’appel de fonction, il sera possible de l’utiliser dans la fonction. Il est important de noter que seuls les derniers arguments peuvent avoir une valeur par défaut. Il est donc inutile pour une fonction à 3 arguments de donner une valeur par défaut au premier argument si les arguments qui suivent n’ont pas de valeur par défaut. Exemple :

const paramFonction = (nom = "John") => {
console.log(`Bienvenue ${nom}`);
}

paramFonction("Benjamin"); // Bienvenue Benjamin
paramFonction(); // Bienvenu John

Opérateur spread

Il est possible d’assigner à des nouvelles variables des valeurs des différents champs d’un objet ou d’un tableau. Il s’agira alors de copies de valeurs (et non de références), ce qui permet de changer la valeur de l’un sans changer la valeur de l’autre. Cependant, ceci ne s’applique qu’au « premier niveau », car le reste consistera en une copie de références. Exemple :

const monObjet = {name: "John", age: 42};
let {name, age} = monObject;

console.log(name); //"John"
name = "Cortana";
console.log(name); //"Cortana"
console.log(monObjet.name); //"John"

//ne marche que pour le premier niveau !!!

const monSuperObjet = {
name: "Cortana",
infos: {
type: "IA",
dateCreation: 2552
}
}

let {name, infos} = monSuperObjet;
console.log(name); //Cortana
infos.type = "Bug";
console.log(monSuperObjet.infos.type);//Bug

const monSuperTableau = ["a", "b"];
const monNouveauTableau = [...monSuperTableau]

monNouveauTableau[0] = "c";
monNouveauTableau[0]; // "c"
monSuperTableau[0]; // "a";

Promesse

Les promesses sont des objets qui permettent de réagir à la réalisation ou à l’échec de cette dernière. Il est possible de faire un appel ajax dans une promesse et on indique quelle fonction exécutée en cas de réussite et quelle fonction exécutée en cas d’échec. Ceci permet d’éviter d’utiliser trop de « callback » puisque généralement c’est la fonction qui crée la promesse qui la gère (ou alors elle renvoie la promesse à une autre fonction). Il est également possible d’attendre la réalisation d’un ensemble de promesses grâce à la fonction Promise.all(). Exemple:

const maPromise = new Promise((resolve, reject) => {
//Traitement, appel réseau, etc...
const error = false;
if(error){
reject(error);
} else {
resolve();
}
});

maPromise.then(() => console.log("Tout fonctionne"))
.catch(err => console.log(`La promesse a échoué à cause de l'erreur ${err}`));
.finally(() => console.log("Finally est toujours exécuté"));

const promise1 = new Promise (resolve => resolve(1));
const promise2 = new Promise (resolve => resolve(2));

const promises = [promise1, promise2];
Promise.all(promises).then(values => {
console.log(values[0]); // 1
console.log(values[1]); // 2
});

Si vous désirez approfondir le sujet, vous pouvez consulter la documentation suivante :

Async/await

Une fonction déclarée « async » est une fonction dont la valeur de retour sera englobée dans une promesse de manière implicite. Dans une fonction « async », il est possible d’utiliser l’instruction « await » devant une promesse pour attendre sa valeur de retour. Ceci permet de réduire le nombre de « callback » de manière considérable et de rendre le code beaucoup plus lisible.

Exemple:

async function maFonctionAsynchrone(){
const promise = new Promise(resolve => resolve(1));
const maVariable = await promise;
return maVariable;
}

//"Une fonction asynchrone renvoie une promesse"
maFonctionAsynchrone().then(value => {
console.log(value); //1
}).catch(err => { // en cas d'erreur pour la promesse qu'on attend, la fonction asynchrone renvoie l'erreur de la promesse;
console.log(err);
});

Lien vers la documentation

Depuis l’ECMAScript 2022, il est possible d’utiliser « await » dans un module sans être dans une fonction asynchrone

Chaînage optionnel

Le chaînage optionnel permet d’éviter les erreurs lorsqu’on essaie d’accéder à des propriétés d’une variable « undefined ». Cela permet d’éviter de faire des « if » pour vérifier que chaque variable soit définie. Si la variable n’est pas définie (« undefined » donc), la valeur « undefined » sera renvoyée plutôt qu’une erreur.

Exemple :

const monObjet = {nom: "John"};
console.log(monObjet.infos?.naissance); // undefined
console.log(monObjet.infos.naissance); // error

Lien vers la documentation

Opérateur de coalescence des nuls

L’opérateur de coalescence des nuls permet d’obtenir une valeur par « défaut » si la valeur testée est « null » ou « undefined ». Il est souvent utilisé avec le chaînage optionnel.

Exemple:

const monObjet = {nom: "John"};
console.log(monObjet.age ?? 42); //monObject.age est undefined donc 42 est renvoyé et utilisé par console.log()

Lien vers la documentation

Méthode at()

Depuis l’ECMAScript 2022, il est possible d’utiliser la méthode at() avec les chaînes de charactères et les tableaux. La méthode tableau.at(i) renverra la même chose que tableau[i]. Cependant, at() supporte les indices négatifs. Ces derniers permettent de compter depuis la fin du tableau. Voici un exemple :

const array1 = [5, 12, 8, 130, 44];

let index = 2;

console.log(`Using an index of ${index} the item returned is ${array1.at(index)}`); // expected output: "Using an index of 2 the item returned is 8"

index = -2;

console.log(`Using an index of ${index} item returned is ${array1.at(index)}`); // expected output: "Using an index of -2 item returned is 130"

Source

toSorted(), toReversed(), toSpliced()

JavaScript possède des méthodes modifiant directement le tableau:

  • sort()
  • reverse()
  • splice()
const mon_tableau = [1, 2, 3];
mon_tableau.reverse(); // le tableau est directement modifié
console.log(mon_tableau) // 3, 2, 1

Le tableau original est perdu si vous n'avez pas copié le tableau préalablement.

JavaScript propose une alternative pour ces méthodes permettant de renvoyer un nouveau tableau en laissant l'ancien intact.

const mon_tableau = [1, 2, 3];
const mon_second_tableau = mon_tableau.toReversed(); // un nouveau tableau est renvoyé et l'ancien n'est pas changé
console.log(mon_tableau); // 1, 2, 3
console.log(mon_second_tableau); // 3, 2, 1

Cette deuxième approche est une meilleure approche selon moi. Elle vous permet de ne prendre aucun risque en gardant le tableau originel. Vous ne devriez plus utiliser les anciennes méthodes et utiliser uniquement les nouvelles, vous éviterez facilement des erreurs.

findLast(), findLastIndex()

Les méthodes find() et findIndex() commencent leur recherche au début du tableau. Dès qu'un élément correspond à la recherche, la fonction renvoie l'élément. Si vous désirez commencer la recherche par la fin du tableau, il suffit d'utiliser les méthodes findLast() et findLastIndex().

Exemple:

const array1 = [5, 12, 50, 130, 44];

const found = array1.findLast((element) => element > 45);

console.log(found);// Expected output: 130

Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast