Le JavaScript et ses runtimes

5mn de lecture

On parle souvent de JavaScript et de runtime JS, mais qu'est-ce que c'est au final ? Qu'est-ce que Node.js, Bun, Deno ou encore "l'Edge" ?

Il est selon moi important de comprendre la différence entre le JavaScript et un runtime afin de réellement saisir ce que l'on fait dans son code.

La spécification du JavaScript

Le JavaScript est un langage qui suit le standard ECMAScript. Ce standard définit comment le langage doit se comporter ainsi que toutes les fonctions disponibles.

C'est ici notamment que de nouvelles fonctionnalités sont ajoutées annuellement, d'où le nom de version ES2015, ES2016, etc.

Ces fonctionnalités sont ajoutées par le TC39. Un organisme neutre qui évalue, note et commente sur les différentes propositions d'ajout ou de modification du langage.

Leur processus est assez simple : il consiste en 6 étapes, commençant au stage-0 et allant jusqu'au stage-4. Ce qu'il faut principalement retenir, c'est que les propositions au stage-3 sont prêtes à être implémentées et testées, et celles au stage-4 sont prêtes à être ajoutée au standard. On peut retrouver une liste des propositions sur leur GitHub.

Cette spécification est implémentée par un moteur JavaScript.

Les moteurs JavaScript

Il faut bien un moyen de lire le code JavaScript que vous écrivez ; c'est le rôle du moteur. Les plus connus sont V8 (Google), SpiderMonkey (Firefox) et JavaScriptCore (Apple).

Le moteur implémente la spécification ECMA et s'occupe donc de comprendre votre code et de l'exécuter via un JIT.

Il y a une notion très importante à comprendre à ce niveau : c'est que la plupart du code que vous écrivez ne fait pas partie de la spécification ECMA. Par exemple, l'API console que vous utilisez lors d'un console.log ne fait aucunement partie du langage. C'est une API définie et implémentée par votre runtime.

Les runtimes JavaScript

Le runtime est l'environnement qui va faire tourner votre code JavaScript. Les plus connus sont ceux des navigateurs, puisque votre navigateur fait tourner du JavaScript ! Côté backend, le plus connu est Node.js qui utilise le moteur V8.

Le runtime ne va pas que faire tourner du code JavaScript, il va également y ajouter des fonctionnalités, comme l'exemple donné plus haut, votre code console.log est en fait implémenté directement dans le runtime. Votre navigateur et Node.js auront deux implémentations complètement différentes de cette API.

C'est le cas de beaucoup d'API ; un autre exemple connu est la méthode fetch. Elle n'était pas disponible dans Node.js avant longtemps car ce n'est pas une API standard du JavaScript, donc rien n'obligeait Node.js à implémenter cette méthode.

Quand on y pense, ça fait complètement sens. Il n'est aucunement utile côté serveur d'avoir accès à l'History API (une API qui donne accès à l'historique du navigateur), par exemple. À l'inverse, avoir accès à la carte réseau est un non-sens dans un navigateur.

Côté navigateur, on peut retrouver la plupart de ces APIs sur la page Web APIs de la doc MDN. Elles sont standardisées par le WHATWG. Côté backend, c'est une autre histoire : il n'y a pas vraiment de standard, puisque Node.js a longtemps été seul dans cet univers. On peut retrouver leur documentation ici.

Le choix d'un runtime dépend souvent de la méthode de déploiement que vous souhaitez utiliser et du fonctionnement de votre application. Par exemple, l'Edge ainsi que les fonctions lambda sont rarement adaptés pour exécuter une application complète, mais plutôt pour des fonctions simples. Chaque runtime présente ses avantages ainsi que ses inconvénients. Si vous ne savez pas lequel choisir, il est généralement préférable d'utiliser Node.js.

Du coup qu'est-ce que Bun ou encore Deno ?

Ce sont d'autres runtimes JavaScript destinés au serveur. Bun repose sur JavaScriptCore, tandis que Deno utilise V8, le même moteur que Node.js.

Contrairement à Node.js, qui est écrit en C++, Bun est développé en Zig et Deno en Rust. Il est également à noter que Deno utilise un système différent de celui de Node.js pour l'asynchronicité (libuv pour Node.js et tokio pour Deno).

Ils ont tous leur propre API pour réaliser certaines tâches, mais comme dit auparavant, il n'existe pas encore de standard pour les APIs serveurs.

Pour le moment, Deno et Bun essaient d'être 100% compatible avec les APIs fournies par Node.js. La raison étant que c'est là que l'écosystème et les développeurs se trouve.

Pourquoi ces runtimes ont été créé ? Pour répondre à certaines limitations perçues de Node.js et pour explorer de nouvelles approches seulement réalisable avec une réécriture complète.

WinterCG

Le WinterCG (Web-interoperable Runtimes Community Group) est un groupe communautaire qui a pour objectif de créer une interopérabilité entre les APIs Web lorsqu'elles s'exécutent dans des environnements autres que le navigateur.

Leur principal objectif est de permettre à votre code de tourner sur l'ensemble des runtimes JavaScript existants sans que vous ayez à le réécrire. Il travail sur un ensemble d'APIs communes que tous les runtimes membres devraient supporter.

C'est pourquoi, sur Node.js, vous avez souvent différents moyens d'effectuer la même tâche : vous avez accès à l'API fournie par Node.js et à l'API issue de WinterCG.

// Node.js API
import { randomUUID } from 'node:crypto'
randomUUID()

// Web Interop
globalThis.crypto.randomUUID()

Bien entendu, toute application légèrement complexe est pour le moment obligée d'utiliser des APIs spécifiques au runtime, que ce soit pour leur performance accrue ou tout simplement parce qu'elles n'existent pas encore de manière standard.

L'Edge

L'Edge n'est pas un runtime en lui-même, mais un principe qui change la façon de distribuer une application. Au lieu de centraliser le déploiement sur un serveur précis, l'Edge consiste à déployer son code au plus près de son client.

Pourquoi en parler dans les runtimes ? Parce que plusieurs runtimes sont connus pour faciliter le déploiement dans "l'Edge". On peut notamment citer les Cloudflare Workers. Ces runtimes présentent certaines limitations, il est donc important de bien les comprendre avant de commencer un projet dessus.

Ce qu'il faut retenir

  • Le langage JavaScript évolue chaque année, il est possible de suivre ses évolutions via les propositions au TC39.
  • Il s'exécute dans un environnement (dit runtime), et il en existe plusieurs. Les plus utilisés sont ceux des navigateurs ainsi que Node.js.
  • La plupart du code que vous écrivez ne fait pas partie de la spécification du JavaScript mais du runtime.
  • Le choix du runtime approprié dépend des besoins spécifiques de votre projet, notamment en termes de performances, de sécurité et de compatibilité avec les outils et bibliothèques existants.