Aller au contenu principal

39 articles tagués avec « ECMAScript »

Voir tous les tags

Références faibles et finalisateurs

· 11 minutes de lecture
Sathya Gunasekaran ([@_gsathya](https://twitter.com/_gsathya)), Mathias Bynens ([@mathias](https://twitter.com/mathias)), Shu-yu Guo ([@_shu](https://twitter.com/_shu)), et Leszek Swirski ([@leszekswirski](https://twitter.com/leszekswirski))

Généralement, les références aux objets sont fortement conservées en JavaScript, ce qui signifie que tant que vous avez une référence à l'objet, il ne sera pas collecté par le garbage collector.

const ref = { x: 42, y: 51 };
// Tant que vous avez accès à `ref` (ou toute autre référence
// au même objet), l'objet ne sera pas collecté par le garbage collector.

Actuellement, les WeakMap et WeakSet sont les seuls moyens de référencer un objet de manière quasi-faible en JavaScript : ajouter un objet en tant que clé à un WeakMap ou WeakSet ne l'empêche pas d'être collecté par le garbage collector.

const wm = new WeakMap();
{
const ref = {};
const metaData = 'foo';
wm.set(ref, metaData);
wm.get(ref);
// → metaData
}
// Nous n'avons plus de référence à `ref` dans ce bloc de portée, donc
// il peut être collecté par le garbage collector, même s'il est une clé
// dans `wm` auquel nous avons encore accès.

Trie stable `Array.prototype.sort`

· 4 minutes de lecture
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Disons que vous avez un tableau de chiens, où chaque chien a un nom et une note. (Si cet exemple vous semble bizarre, sachez qu'il existe un compte Twitter qui se spécialise exactement là-dedans… Ne demandez pas !)

// Notez comment le tableau est pré-trié par ordre alphabétique par `name`.
const doggos = [
{ name: 'Abby', rating: 12 },
{ name: 'Bandit', rating: 13 },
{ name: 'Choco', rating: 14 },
{ name: 'Daisy', rating: 12 },
{ name: 'Elmo', rating: 12 },
{ name: 'Falco', rating: 13 },
{ name: 'Ghost', rating: 14 },
];
// Triez les chiens par `rating` en ordre décroissant.
// (Cela met à jour `doggos` en place.)
doggos.sort((a, b) => b.rating - a.rating);

`Symbol.prototype.description`

· Une minute de lecture
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Les Symbol en JavaScript peuvent recevoir une description lors de leur création :

const symbol = Symbol('foo');
// ^^^^^

Auparavant, la seule façon d'accéder à cette description par programme était de manière indirecte via Symbol.prototype.toString() :

const symbol = Symbol('foo');
// ^^^^^
symbol.toString();
// → 'Symbol(foo)'
// ^^^
symbol.toString().slice(7, -1); // 🤔
// → 'foo'

Cependant, ce code semble légèrement magique, pas très explicite, et viole le principe « exprimer l'intention, non l'implémentation ». Cette technique ne permet pas non plus de faire la distinction entre un symbole sans description (c'est-à-dire Symbol()) et un symbole dont la description est une chaîne vide (c'est-à-dire Symbol('')).

`Object.fromEntries`

· 4 minutes de lecture
Mathias Bynens ([@mathias](https://twitter.com/mathias)), spécialiste en JavaScript

Object.fromEntries est un ajout utile à la bibliothèque JavaScript intégrée. Avant d'expliquer ce qu'il fait, il est utile de comprendre l'API existante Object.entries.

Object.entries

L'API Object.entries existe depuis un certain temps.

Pour chaque paire clé-valeur dans un objet, Object.entries vous donne un tableau où le premier élément est la clé et le second élément est la valeur.

Object.entries est particulièrement utile en combinaison avec for-of, car il vous permet d'itérer très élégamment sur toutes les paires clé-valeur d'un objet :

const object = { x: 42, y: 50 };
const entries = Object.entries(object);
// → [['x', 42], ['y', 50]]

for (const [key, value] of entries) {
console.log(`La valeur de ${key} est ${value}.`);
}
// Affiche :
// La valeur de x est 42.
// La valeur de y est 50.

Malheureusement, il n'y a pas de moyen facile de revenir du résultat des entries à un objet équivalent… jusqu'à maintenant !

Object.fromEntries

La nouvelle API Object.fromEntries effectue l'inverse de Object.entries. Cela facilite la reconstruction d'un objet à partir de ses entries :

const object = { x: 42, y: 50 };
const entries = Object.entries(object);
// → [['x', 42], ['y', 50]]

const result = Object.fromEntries(entries);
// → { x: 42, y: 50 }

Un cas d'utilisation courant est la transformation d'objets. Vous pouvez maintenant faire cela en itérant sur ses entries, puis en utilisant des méthodes de tableau que vous connaissez probablement déjà :

const object = { x: 42, y: 50, abc: 9001 };
const result = Object.fromEntries(
Object.entries(object)
.filter(([ key, value ]) => key.length === 1)
.map(([ key, value ]) => [ key, value * 2 ])
);
// → { x: 84, y: 100 }

Dans cet exemple, nous appliquons un filter sur l'objet pour ne récupérer que les clés de longueur 1, c'est-à-dire seulement les clés x et y, mais pas la clé abc. Nous effectuons ensuite un map sur les entries restantes et retournons une paire clé-valeur mise à jour pour chacune. Dans cet exemple, nous doublons chaque valeur en la multipliant par 2. Le résultat final est un nouvel objet, avec uniquement les propriétés x et y, et les nouvelles valeurs.

Promise combinators

· 5 minutes de lecture
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Depuis l'introduction des promesses dans ES2015, JavaScript a pris en charge exactement deux combinateurs de promesses : les méthodes statiques Promise.all et Promise.race.

Deux nouvelles propositions sont actuellement en cours de standardisation : Promise.allSettled et Promise.any. Avec ces ajouts, il y aura un total de quatre combinateurs de promesses en JavaScript, chacun permettant différents cas d'utilisation.

`Array.prototype.flat` et `Array.prototype.flatMap`

· 2 minutes de lecture
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Array.prototype.flat

Le tableau dans cet exemple est profond de plusieurs niveaux : il contient un tableau qui lui-même contient un autre tableau.

const array = [1, [2, [3]]];
// ^^^^^^^^^^^^^ tableau extérieur
// ^^^^^^^^ tableau intérieur
// ^^^ tableau le plus intérieur

Array#flat retourne une version aplatie d'un tableau donné.

array.flat();
// → [1, 2, [3]]

// …est équivalent à :
array.flat(1);
// → [1, 2, [3]]

La profondeur par défaut est 1, mais vous pouvez passer n'importe quel nombre pour aplatir récursivement jusqu'à cette profondeur. Pour continuer d'aplatir récursivement jusqu'à ce que le résultat ne contienne plus de tableaux imbriqués, on passe Infinity.

// Aplatir récursivement jusqu'à ce que le tableau ne contienne plus de tableaux imbriqués :
array.flat(Infinity);
// → [1, 2, 3]

Pourquoi cette méthode s'appelle-t-elle Array.prototype.flat et non Array.prototype.flatten ? Lisez notre article sur le #SmooshGate pour le découvrir !

Array.prototype.flatMap

Voici un autre exemple. Nous avons une fonction duplicate qui prend une valeur et retourne un tableau contenant cette valeur deux fois. Si nous appliquons duplicate à chaque valeur d'un tableau, nous obtenons un tableau imbriqué.

const duplicate = (x) => [x, x];

[2, 3, 4].map(duplicate);
// → [[2, 2], [3, 3], [4, 4]]

Vous pouvez ensuite appeler flat sur le résultat pour aplatir le tableau :

[2, 3, 4].map(duplicate).flat(); // 🐌
// → [2, 2, 3, 3, 4, 4]

Étant donné que ce schéma est très courant en programmation fonctionnelle, il existe maintenant une méthode dédiée flatMap pour cela.

[2, 3, 4].flatMap(duplicate); // 🚀
// → [2, 2, 3, 3, 4, 4]

flatMap est un peu plus efficace que d'effectuer un map suivi d'un flat séparément.

Vous êtes intéressé par les cas d'utilisation de flatMap ? Découvrez l'explication d'Axel Rauschmayer.

Prise en charge de Array#{flat,flatMap}

Séparateurs numériques

· 2 minutes de lecture
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Les grands littéraux numériques sont difficiles à analyser rapidement pour l'œil humain, surtout lorsqu'il y a beaucoup de chiffres répétitifs :

1000000000000
1019436871.42

Pour améliorer la lisibilité, une nouvelle fonctionnalité de langage JavaScript permet d'utiliser des underscores comme séparateurs dans les littéraux numériques. Ainsi, cela peut désormais être réécrit en regroupant les chiffres par mille, par exemple :

`String.prototype.matchAll`

· 3 minutes de lecture
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Il est courant d'appliquer à plusieurs reprises la même expression régulière sur une chaîne pour obtenir toutes les correspondances. Dans une certaine mesure, cela est déjà possible aujourd'hui en utilisant la méthode String#match.

Dans cet exemple, nous trouvons tous les mots constitués uniquement de chiffres hexadécimaux, puis nous enregistrons chaque correspondance :

const string = 'Nombres hex magiques : DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.match(regex)) {
console.log(match);
}

// Résultat :
//
// 'DEADBEEF'
// 'CAFE'

Cependant, cela ne vous donne que les sous-chaînes qui correspondent. Habituellement, vous ne voulez pas seulement les sous-chaînes, mais aussi des informations supplémentaires telles que l'index de chaque sous-chaîne ou les groupes capturés dans chaque correspondance.

Il est déjà possible de faire cela en écrivant votre propre boucle et en suivant vous-même les objets correspondants, mais cela peut être un peu irritant et pas très pratique :

const string = 'Nombres hex magiques : DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
console.log(match);
}

// Résultat :
//
// [ 'DEADBEEF', index: 19, input: 'Nombres hex magiques : DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Nombres hex magiques : DEADBEEF CAFE' ]

La nouvelle API String#matchAll rend cela plus facile que jamais : vous pouvez désormais écrire une simple boucle for-of pour obtenir tous les objets correspondants.

const string = 'Nombres hex magiques : DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.matchAll(regex)) {
console.log(match);
}

// Résultat :
//
// [ 'DEADBEEF', index: 19, input: 'Nombres hex magiques : DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Nombres hex magiques : DEADBEEF CAFE' ]

String#matchAll est particulièrement utile pour les expressions régulières avec des groupes de capture. Elle vous donne des informations complètes pour chaque correspondance individuelle, y compris les groupes capturés.

const string = 'Dépôts GitHub préférés : tc39/ecma262 v8/v8.dev';
const regex = /\b(?<owner>[a-z0-9]+)\/(?<repo>[a-z0-9\.]+)\b/g;
for (const match of string.matchAll(regex)) {
console.log(`${match[0]} at ${match.index} with '${match.input}'`);
console.log(`→ propriétaire : ${match.groups.owner}`);
console.log(`→ dépôt : ${match.groups.repo}`);
}

Exports de l'espace de noms des modules

· Une minute de lecture
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Dans les modules JavaScript, il était déjà possible d'utiliser la syntaxe suivante :

import * as utils from './utils.mjs';

Cependant, aucune syntaxe d'exportation symétrique n'existait… jusqu'à maintenant :

export * as utils from './utils.mjs';

Cela équivaut à ce qui suit :

import * as utils from './utils.mjs';
export { utils };

Champs de classe publics et privés

· 5 minutes de lecture
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Plusieurs propositions étendent la syntaxe existante des classes JavaScript avec de nouvelles fonctionnalités. Cet article explique la nouvelle syntaxe des champs de classe publics dans V8 v7.2 et Chrome 72, ainsi que la syntaxe des champs de classe privés à venir.

Voici un exemple de code qui crée une instance d'une classe nommée IncreasingCounter :

const counter = new IncreasingCounter();
counter.value;
// affiche 'Récupération de la valeur actuelle !'
// → 0
counter.increment();
counter.value;
// affiche 'Récupération de la valeur actuelle !'
// → 1

Notez que l'accès à la propriété value exécute du code (c'est-à-dire qu'il affiche un message) avant de retourner le résultat. Maintenant demandez-vous, comment implémenteriez-vous cette classe en JavaScript ? 🤔

Syntaxe des classes ES2015

Voici comment IncreasingCounter pourrait être implémenté en utilisant la syntaxe des classes ES2015 :

class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Récupération de la valeur actuelle !');
return this._count;
}
increment() {
this._count++;
}
}

La classe installe le getter value et une méthode increment sur le prototype. Plus intéressant encore, la classe dispose d'un constructeur qui crée une propriété d'instance _count et définit sa valeur par défaut à 0. Nous avons actuellement tendance à utiliser le préfixe de soulignement pour indiquer que _count ne doit pas être utilisé directement par les consommateurs de la classe, mais ce n'est qu'une convention ; il ne s'agit pas vraiment d'une propriété « privée » avec des sémantiques spéciales appliquées par le langage.