JavaScript, les bons éléments, façon 2020 : Ajouts syntaxiques

Au fur et à mesure des années, surtout depuis ES6, de nombreuses petites choses ont été ajoutées à JavaScript qui rendent le code plus joli, plus naturel, mais qui ne révolutionnent pas directement l’écriture : Mises toutes ensembles avec les autres fonctionnalités, elles font néanmoins de JavaScript un langage plus puissant.

L’opérateur **

Cet opérateur a été conçu pour se passer du vieux Math.pow(), purement fonctionnel. Il vient "en extension" de l’opérateur * (multiplier) qui, en JavaScript, ne sert qu’aux nombres (et à déclarer des générateurs).

// Avant 
Math.pow(2, 5); // 32

// Maintenant
2 ** 5; // 32

La déstructuration d’objets

De la même manière que l’on peut déstructurer les itérables (on l’a vu dans la partie sur les itérateurs), une fonctionnalité similaire a été offerte aux objets.

La première chose liée à cette déstructuration est qu’il est possible de nommer des variables directement via les clés d’un objet :

const jimmy = { age: 18, city: 'London', country: 'UK' };

// Déclaration de age et city depuis l'objet {jimmy}
const { age, city } = jimmy;

// Il est possible de renommer lors de la déclaration
const { country: jimmy_country } = jimmy; 

console.log(jimmy_country); // 'UK'
console.log(age); // 18
console.log(city); // 'London'

Cette syntaxe est également valide en profondeur et en association avec la déstructuration d’itérables :

const data = {
  count: 3,
  human: {
    name: 'Jimmy',
    task_ids: [3, 14, 2],
  },
};

const { count, human: { name, task_ids: [id_1, id_2] } } = data;

console.log(count); // 3
console.log(name); // 'Jimmy'
console.log(id_1); // 3
console.log(id_2); // 14

Vous pouvez déstructurer des objets avec ... dans la déclaration d’autres objets pour les copier :

const first = { a: 3, b: 5 };
const second = { ...first, c: 18 };

console.log(second); // { a: 3, b: 5, c: 18 }

// La définition de {second} est totalement équivalente à :
const second = Object.assign({}, first, { c: 18 });

Syntaxe de propriété concise

Il vous est sûrement déjà arrivé de définir des propriétés d’un nouvel objet depuis une variable, dont le nom est le même que celui de la propriété définie.

JavaScript fournit maintenant une syntaxe raccourcie pour cette opération :

const name = 'Jimmy';
const age = 18;

const human = { name, age };
// totalement égal à
const human = { name: name, age: age };

console.log(human); // { name: 'Jimmy', age: 18 }

Cette syntaxe peut être mixée avec la déclaration "classique" de propriété, voyez la simplement comme raccourci au { prop: prop }.

Propriétés calculées lors de la déclaration d’objets

Un autre problème lors de la déclaration d’objet est le choix délibéré de pouvoir taper les noms de propriété dans préciser de délimiteur de chaîne de caractère (de guillemet, quoi).

Ce choix cause le problème que lorsqu’on a envie d’utiliser la valeur d’une variable comme clé, on est obligé de déclarer l’objet, puis d’utiliser l’opérateur [] avec notre valeur pour déclarer notre propriété dite "calculée".

const prop_name = 'the_name';

const human = {
  age: 18,
};
// On était obligé de déclarer de notre prop après l'objet
human[prop_name] = 'Jimmy';

human.the_name; // 'Jimmy'

JavaScript ES6 fournit un raccourci pour cela dès la déclaration, entourez le nom de votre variable de crochets :

const prop_name = 'the_name';

const human = {
  age: 18,
  [prop_name]: 'Jimmy',
};

human.the_name; // 'Jimmy'

Déclaration rapide de méthodes

Dernière "update" pour la syntaxe de déclaration des objets, on peut désormais très rapidement déclarer des méthodes de manière jolie !

Avant, on devait préciser le nom de la méthode, puis lui associer une fonction anonyme :

const human = {
  name: 'Jimmy',
  eat: function () {
    console.log(this.name, 'mange');
  },
};

On peut désormais simplement utiliser le nom de la méthode suivi de parenthèses. Les paramètres peuvent être précisés dans les parenthèses.

const human = {
  name: 'Jimmy',
  eat() {
    console.log(this.name, 'mange');
  },
};

Chaînage optionnel

Le chaînage optionnel, apparu en fin 2019 en JavaScript, est certainement la nouveauté la plus importante de cette liste.

Elle permet d’accéder à des sous-propriétés d’objets / d’appeler des fonctions de manière sûre, en s’assurant que les propriétés accédées ne valent ni null ni undefined.

La syntaxe est la suivante :

const obj = { 
  prop1: { 
    subprop: 3,
  } 
};

// Ici, on accède qu'à des choses déjà définies : le résultat est celui escompté (3)
const count = obj?.prop1?.subprop; // 3

// Cela équivaut à marquer
const count = (obj !== null && obj !== undefined ? 
  (obj.prop1 !== null && obj.prop1 !== undefined ? 
    obj.prop1.subprop : 
    undefined
  ) : 
  undefined
);

// nonexistant vaut undefined, il n'y a pas d'erreur lancée !
const nonexistant = obj.prop2?.test;

// Sans utiliser ?., on aurait eu une TypeError à l'accès à .test,
// car on tenterait d'accéder à une propriété de undefined, ce qui est une erreur

Cette fonctionnalité est très utile, notamment pour les résultats d’une API, où des clés peuvent être parfois manquantes. Attention, la fonctionnalité de vérification d’existance n’est faite que là où un ?. est précisé. Si une erreur à l’accès survient avant ou après, il ne vous en protègera pas.

Sachez également (ce n’est pas très joli, je ne vais donc pas m’étendre) que cet opérateur peut être utilisé pour utiliser l’opérateur d’indexation ([]) ou d’appel de fonction (()).

// Oui, ça s'écrit vraiment comme ça
obj?.['prop1']; // { subprop: 3 }

// Ne fait rien, prop2 n'existe pas, 
// donc l'appel de fonction n'est pas déclenché
obj.prop2?.();

Opérateur de "Null-coalescing"

En JavaScript, il est très commun, notamment lors de l’utilisateur de valeurs par défaut, d’assigner une valeur contenue dans une variable, mais d’assigner autre chose si cette variable est null ou undefined.

const valeur = possiblement_null ?? valeur_si_null;

Auparavent, on pouvait tricher avec l’opérateur || (qui renvoie la première opérande si elle est truthy, c’est à dire si elle est équivalente à true, sinon il renvoie la seconde). Cet opérateur avait cependant un effet de bord : Si la variable à sa gauche valait false, il renvoie l’expression de droite. C’est attendu, c’est même voulu, mais das le cadre des paramètres par défaut, ça peut poser problème.

function handleData(data, options = {}) {
  const parse_json = options.parse_json || true;

  if (parse_json) {
    return JSON.parse(data);
  }
  return data;
}

handleData('hello', { parse_json: false });

Vous voyez le problème ici ? Si on passe correctement l’objet options avec une propriété parse_json à false, alors elle sera écrasée par true ! Ce n’est pas ce qu’on veut.

function handleData(data, options = {}) {
  const parse_json = options.parse_json ?? true;

  if (parse_json) {
    return JSON.parse(data);
  }
  return data;
}

handleData('hello', { parse_json: false });

Ici, tout se passe comme prévu ! Si on ne passe pas options, alors parse_json vaut undefined (la propriété n’existe pas), donc true est utilisé par défaut. Si on passe explicitement false, alors elle est récupérée, car elle ne vaut ni null ni undefined.

Alors, pensez à l’opérateur ?? dans ce genre de cas, il est souvent plus approprié que || !

Séparateur numérique des litérals

On parle ici d’une nouveauté purement visuelle, elle ne sera strictement à rien, à part rendre votre code plus lisible. Comme le null coalescing et l’opérateur ?., c’est très récent (en 2020), évitez de l’utiliser sans passer par un transpileur.

Vous pouvez séparer des parties d’un litéral de nombre avec _, un peu comme en Java.

const a_million = 1_000_000;
// Pareil, mais moins lisible
const a_million = 1000000;

C’est tout 😀

Le binding optionnel de variable dans la clause catch

Pour terminer cette série, on va parler de la plus petite nouveauté : Le binding optionnel dans catch.

Désormais, vous n’êtes plus obligé de capturer l’erreur dans une variable dans une clause catch si elle ne vous intéresse pas, ne spécifiez juste pas les parenthèses :

try {
  somethingRisky();
} catch (e) {
  // e est bindé à l'erreur lancée
}

// Maintenant, ceci est possible
try {
  somethingRisky();
} catch {
  // On se fiche de l'erreur, on veut juste
  // traiter le cas où ça crash
}

La fin

Cette petite partie est terminée.

La suivante sera à propos de, sans doute, la plus importante nouveauté apportée à JavaScript : les promesses.

À bientôt 😀 !

Article précédent : Function updateArticle suivant : L’asynchronicité

Laisser un commentaire