A excesiva fragmentación de JavaScript

Por Óscar Otero —

JavaScript é unha linguaxe de programación peculiar, comezando polo seu propio nome que está rexistrado por Oracle, polo que legalmente non lle podemos chamar «JavaScript». De feito, o organismo que se encarga do seu estándar é ECMA Internacional, que desenvolve o estándar ECMAScript, a través do comité técnico TC39.

A segunda peculiaridade son as diferentes implantacións. ECMAScript é un estándar xenérico e agnóstico, sendo JavaScript a máis grande e popular implantación pero existen algunhas máis. Por exemplo, ActionScript —a linguaxe que tiña Flash— tamén estaba baseada en ECMAScript.

Pero incluso se deixamos de lado as outras implantacións e nos centramos só en JavaScript, tamén temos unha grande variedade de «sabores», que dependen basicamente de dúas variantes: o motor e o entorno.

O motor (ou JavaScript engine) é o software que se encarga de ler código JavaScript, compilalo e executalo. O máis famoso é V8, creado por Google e que se atopa en todos os navegadores baseados en Chromium como Chrome, Brave, Edge, Vivaldi, etc. Firefox ten o seu propio motor (SpiderMonkey) e Safari tamén (JavaScriptCore). Pero existen moitos outros motores, algúns deles totalmente obsoletos e outros novos experimentais. Podes atopar a maioría deles nesta páxina.

Pero o motor por si só pouco pode facer. Ao igual que o motor dun coche, precisa dun entorno que lle permita acceder ao «mundo real». Por exemplo, o JavaScript dentro dun navegador debe poder acceder á páxina web que estamos vendo para interactuar con ela (por exemplo, escoitar o evento click sobre un botón), utilizar diferentes APIs proporcionadas polo navegador (xeolocalización, subida de ficheiros, facer peticións HTTP, etc).

Hai outro tipo de entornos, chamados runtime, que son os que se executan fora dun navegador como pode ser Node. Aquí non existe unha páxina web, senón APIs para acceder a servizos do sistema operativo (ler arquivos do disco duro, executar scripts, levantar un servidor HTTP, etc). Por iso a combinación de motores e entornos lévanos a formas de executar JavaScript diferentes e incompatibles entre si. A seguinte táboa mostra só uns poucos casos de exemplo, pero a cantidade de combinacións existentes é enorme.

Navegadores Web Runtimes
V8 Chrome, Edge, Brave, Vivaldi, etc Node, Deno, Workerd
JavaScriptCore Safari Bun
SpiderMonkey Firefox, Servo, Zen
Nova Andromeda
QuickJS Edbrowse Txiki
LibJS Ladybird SerenityOS

O código JavaScript destinado a navegadores Web adoita funcionar ben independentemente do motor (falo sobre todo de motores xa maduros como os tres primeiros da táboa), xa que a maioría implantan as APIs Web estándar. É certo que pode haber incompatibilidades, sobre todo con navegadores antigos ou APIs demasiado modernas, pero cada vez existen menos diferencias e diría que na maioría dos casos son intercambiables.

Os maiores problemas de compatibilidade atopámolos na outra columna. Escribir código que funcione tanto en navegadores como runtimes é complexo, pero tamén o é escribir código que sexa compatible entre os diferentes runtimes.

Existe un comité técnico, o WinterTC adicado a definir un mínimo de compatibilidade entre runtimes sobre todo naquelas APIs que tamén existen na Web. Por exemplo, se no navegador usamos a función fetch() para facer unha petición HTTP, por que non ter esa mesma función en Node, Deno, Bun etc e así facer o noso código máis compatible?

A realidade é que o traballo de estandarización é lento e complexo debido á excesiva fragmentación. Existe unha proposta para definir runtime keys, unha especie de identificador único por cada runtime. Sen contar os navegadores web, na web teñen máis de 20 implantacións diferentes. Vexo case imposible poñer de acordo a todos eles, cada un coa súa axenda, recursos e prioridades. Ademais, tendo en conta os avances que houbo nos últimos anos, parece que esta interoperabilidade vai levar bastante tempo.