8 nouvelles fonctionnalités JavaScript que vous avez peut-être manquées

Le mastodonte JavaScript ne s’arrête jamais. Il y a un certain nombre de fonctionnalités introduites par la «spécification vivante» chaque année. Ensuite, cela prend du temps pour les navigateurs, Node.js, etc. pour les intégrer, et avant que vous ne vous en rendiez compte, il y a un gros tas de nouvelles fonctionnalités JavaScript que vous n’avez pas essayées.

Ce n’est pas trop tard. Ici, j’ai rassemblé les fonctionnalités ES11 (ECMAScript 11, alias ECMAScript 2020) que vous avez peut-être manquées. Ils comprennent un certain nombre d’améliorations ergonomiques et d’autres améliorations modernes. Nous allons jeter un coup d’oeil.

Chaînage optionnel

Le chaînage optionnel est l’une de ces subtilités simples mais efficaces qui simplifient un peu la vie. Cette fonctionnalité vous permet de naviguer dans les chaînes d’objets et de fonctions avec un raccourci pour traiter les valeurs nulles ou indéfinies.

Le chaînage facultatif fonctionne pour des cas simples comme les données d’objets imbriqués, pour les fonctions qui renvoient des valeurs inexistantes et même pour les fonctions qui n’existent pas en tant que méthodes membres sur des objets. Vous pouvez donc faire des choses comme nous le voyons dans la liste 1.

Listing 1. Exemples de chaînage facultatif

box = {
  innerBox: {},
  nullFunction: function() { return null; },
  // non-existent method foo() and members bar
}
// with optional chaining:
if (box?.innerBox?.foo){ }// navigate object graph safely
// old style without optional chaining:
if (box.innerBox && box.innerBox.foo){ }
//also works for functions:
box.nullFunction()?.foo
// and nonexistent methods and members:
box?.foo() && box?.bar

Le code est plus concis pour exprimer exactement ce que vous voulez : si l’objet existe, accédez-y ; sinon, renvoie undefined.

globalThis

Un autre ajout judicieux au langage de base, globalThis, est une abstraction de l’objet global disponible pour le contexte courant. Le plus connu et le plus ancien d’entre eux est l’objet window trouvé dans JavaScript exécuté dans le navigateur. Mais les environnements comme Node.js et les web workers ont leurs propres objets racine qui servent exactement le même objectif : global et self, respectivement.

globalThis rend le code plus portable (et élimine les vérifications d’existence) en enveloppant tous ces objets racine dans un seul identifiant qui se résout en objet racine global dans n’importe quel environnement dans lequel le code s’exécute.

BigInt

Avant ES11, le plus grand entier pouvant être référencé en JavaScript en toute sécurité était Number.MAX_SAFE_INTEGER, qui se résout en 9007199254740991 (alias 2^53 – 1). Ce n’est peut-être pas un problème quotidien pour certains d’entre nous, mais pour de nombreuses applications, il s’agit d’une ampleur amusante qui oblige les programmeurs à utiliser un wrapper comme un grand entier. (Notez que la bibliothèque big-integer est toujours utile en tant que polyfill.)

Lorsque vous utilisez le type numérique traditionnel pour représenter des nombres aussi grands, vous rencontrerez des arrondis inattendus. (Notez que ces commentaires s’appliquent tous à très petit nombres également, c’est-à-dire -2^53 – 1).

Avec ES11, le type BigInt est intégré pour ces scénarios. Vous pouvez le définir en ajoutant un n à la fin d’un nombre, comme dans mySafeBigNumber = 9007199254740992n.

Il s’agit vraiment d’un nouveau type, et pas seulement d’un tour de passe-passe autour du type Number existant. Si tu fais typeof 12vous obtenez number. Si tu fais typeof mySafeBigNumbervous obtenez bigint. De plus, si vous essayez quelque chose de néfaste comme mySafeBigNumber - 12vous obtiendrez une erreur : “Impossible de mélanger BigInt et d’autres types, utilisez des conversions explicites.”

Pour convertir entre les deux, vous pouvez utiliser les constructeurs. Par exemple, vous pourriez écrire let myUnsfeBigNumber = Number(mySafeBigNumber), mais attendez — vous ne devriez pas faire cela parce que vous venez de créer un objet Number trop grand. Donc, à la place, ne convertissez que lorsque le BigInt a été réduit à une valeur inférieure à la valeur MAX_SAFE_INTEGER.

Dans le même ordre d’idées, il convient de souligner que la comparaison de Numbers et de BigInts pour l’égalité renverra toujours false, car ce sont des types différents.

BigInt prend également en charge la représentation en notation binaire, oct et hexadécimale et prend en charge tous les opérateurs mathématiques typiques auxquels vous pourriez vous attendre. à l’exception l’opérateur unaire plus, dont le but est de convertir une valeur en nombre. La raison en est plutôt obscure, en évitant de casser les changements au code asm non-JS existant.

Enfin, il peut sembler redondant de le souligner, puisque c’est dans le nom même, mais BigInt représente entiers. Exécuter le code x = 3n; x = x / 2n; donnera une valeur de 1n pour x. Les BigInts éliminent tranquillement la partie fractionnaire des nombres.

Coalescence nulle

La coalescence nulle est la plus poétiquement nommée des fonctionnalités ES11 et rejoint le chaînage optionnel pour nous aider dans nos relations avec les valeurs nulles. La coalescence nulle est aussi un nouveau symbole, le double point d’interrogation : ??. Il s’agit d’un opérateur logique avec un comportement similaire à l’opérateur logique OU (le symbole du double tuyau ||).

La différence entre ?? et || est dans la façon dont l’opérateur gère les valeurs nulles par rapport aux valeurs fausses. La plupart des développeurs JavaScript savent comment le langage traite les valeurs non booléennes lorsqu’ils les testent comme vrai/faux (en bref, les chaînes fausses, 0, nulles, vides et indéfinies sont considérées comme fausses et tout le reste se résout en vrai ; plus de détails ici). Nous en profiterons souvent pour tester l’existence d’une chose et si elle n’existe pas, alors utiliser quelque chose d’autre, comme ceci :

let meaningOfLife = answer || 42;

Cela permet de définir une sorte de valeur par défaut tout en testant rapidement l’existence de quelque chose dans answer. Cela fonctionne très bien si nous voulons vraiment le meaningOfLife par défaut à 42 si l’une des valeurs fausses est définie sur answer. Mais que se passe-t-il si vous ne souhaitez revenir à 42 que s’il existe une valeur nulle réelle (nulle ou indéfinie, en particulier) ?

?? rend cela simple. Tu utilises

let meaningOfLife = answer ?? 42;

Pour clarifier cela, pensez à définir 0 comme valeur sur answeret comment attribuer une valeur à meaningOfLife fonctionnerait en utilisant || contre ?? comme dans le Listing 2. Dans ce cas, nous voulons garder 0 comme valeur si elle est définie, mais utiliser 42 si answer est en fait vide.

Listing 2. La coalescence nulle en action

let answer = 0;
let meaningOfLife = answer ?? 42; // meaningOfLife === 0 - what we want
let meaningOfLife = answer || 42; // meaningOfLife === 42 - not what we want
let answer = undefined;
let meaningOfLife = answer ?? 42; // meaningOfLife === 42 - what we want
let meaningOfLife = answer || 42; // meaningOfLife === 42 - also what we want

String.prototype.matchAll

La spécification ES11 ajoute une nouvelle méthode au prototype String : matchAll. Cette méthode applique une expression régulière à l’instance String et renvoie un itérateur avec tous les résultats. Par exemple, supposons que vous vouliez analyser une chaîne pour tous les endroits où un mot commence par t ou T. Vous pouvez le faire comme dans le Listing 3.

Listing 3. Utiliser matchAll

let text = "The best time to plant a tree was 20 years ago. The second best time is now.";
let regex = /(?:^|s)(t[a-z0-9]w*)/gi; // matches words starting with t, case insensitive
let result = text.matchAll(regex);
for (match of result) {
  console.log(match[1]);
}

Mettez de côté la densité inhérente de la syntaxe regex et acceptez que l’expression régulière définie dans le Listing 3 trouve des mots commençant par t ou T. matchAll() applique cette expression régulière à la chaîne et vous renvoie un itérateur qui vous permet de parcourir simplement et facilement les résultats et d’accéder aux groupes correspondants.

Importations dynamiques

ES11 introduit une avancée dans la façon dont vous pouvez importer des modules, en permettant le placement arbitraire des importations qui sont chargées de manière asynchrone. Ceci est également connu sous le nom de fractionnement de code, quelque chose que nous faisons via des outils de construction depuis des années maintenant. Les importations dynamiques sont un autre exemple de la spécification qui rattrape les pratiques dans la nature.

Un exemple simple de syntaxe est présenté dans le Listing 4.

Listing 4. Importation de module asynchrone

let asyncModule = await import('/lib/my-module.ts');

Ce type d’importation peut apparaître n’importe où dans votre code JS, y compris en réponse à des événements utilisateur. Cela facilite le chargement différé des modules uniquement lorsqu’ils sont réellement nécessaires.

Promise.allSettled()

La promise.allSettled() La méthode vous permet d’observer les résultats d’un ensemble de promesses, qu’elles soient remplies ou rejetées. Ceci peut être opposé à promise.all()qui se terminera par une promesse rejetée ou une non-promesse erronée. promise.allSettled() renvoie un tableau d’objets décrivant les résultats de chaque promesse.

Ceci est utile lors de la surveillance d’un groupe de promesses non liées. Autrement dit, vous voulez savoir ce qui leur est arrivé à tous, même si certains échouent au milieu. Le Listing 5 contient un exemple.

Listing 5. exemple promise.allSettled()

let promise1 = Promise.resolve("OK");
let promise2 = Promise.reject("Not OK");
let promise3 = Promise.resolve("After not ok");
Promise.allSettled([promise1, promise2, promise3])
    .then((results) => console.log(results))
    .catch((err) => console.log("error: " + err));

La prise lambda ne se déclenche pas dans ce cas (ce serait le cas si nous avions utilisé promise.all). Au lieu de cela, le then La clause s’exécute et un tableau avec le contenu du Listing 6 est renvoyé. La clé à retenir ici est que la troisième promesse a été exécutée et nous pouvons voir son résultat même si promise2 a échoué avant cela.

Listing 6. Résultats de promise.allSettled()

[
  {"status":"fulfilled","value":"OK"},
  {"status":"rejected","reason":"Not OK"},
  {"status":"fulfilled","value":"After not ok"}
]

Exporter la syntaxe en étoile

Cette fonctionnalité ajoute la possibilité de export * d’un module. Tu pourrais déjà import * d’un autre module, mais maintenant vous pouvez exporter avec la même syntaxe, comme dans le Listing 7.

Listing 7. export * exemple

Export * from '/dir/another-module.js'

Il s’agit d’une sorte de chaînage de modules, vous permettant d’exporter tout depuis un autre module depuis l’intérieur du module actuel. Utile si vous construisez un module qui unit d’autres modules dans son API, par exemple.

Standardisation de la commande for-in

Saviez-vous que l’ordre d’énumération des collections dans les boucles for-in en JavaScript n’était pas garanti ? En fait, il était garanti par tous les environnements JavaScript (navigateurs, Node.js, etc.), mais maintenant cette norme de facto a été absorbée par la spécification. La théorie imite à nouveau la pratique.

Une spécification en évolution

L’un des aspects les plus intéressants du travail dans l’industrie du logiciel consiste à observer l’évolution des choses. Les langages de programmation sont l’une des expressions les plus fondamentales de toutes, où une grande partie de la philosophie de l’informatique rencontre la réalité du codage au jour le jour. JavaScript (comme d’autres langages vivants) continue de croître en réponse à ces forces avec son calendrier de publication annuel.

Copyright © 2022 IDG Communications, Inc.

Leave a Comment