Esta serie de artículos pretende resumir las bases teóricas del lenguaje, desde sus orígenes hasta cómo funcionan sus entresijos. En esta primera parte, vamos a conocer un poco la historia de JavaScript y de sus motores de ejecución.
Historia resumida de JavaScript
JavaScript es un lenguaje interpretado, lo cual significa que no hace falta compilar su código fuente antes de enviarlo al navegador. El intérprete de JavaScript puede ejecutar el código en crudo.
JavaScript es también un lenguaje dinámicamente tipado, lo cual significa que las variables que creamos pueden contener cualquier tipo de dato soportado, ya sean variables simples o estructuras de datos.
El tipado dinámico hace que el rendimiento del lenguaje en ejecución se vea negativamente afectado. Los lenguajes como C, C++ o Java tienen un tipado estático, donde hay que definir qué tipo de variables creamos e incluso el tamaño que van a ocupar. Aunque pueda parecer un engorro, esto hace que tengan un mejor rendimiento al producir un código máquina más eficiente.
Durante los inicios del mundo web, las páginas eran estáticas y no había ningún tipo de dinamismo o posibilidad de interacción. Para solventar esto, un tal Brandam Eich diseñó en 10 días algo llamado LiveScript, lo que posteriormente pasaría a renombrarse JavaScript. Esto solo funcionaba en un navegador que quizás te suene: Netscape.
Al diseñar JavaScript no se tuvo en cuenta el rendimiento del lenguaje, solo tenía que funcionar en el navegador y proveer mecanismos para interactuar con el DOM. Otros navegadores trataron de hacer algo parecido, pero evidentemente cada uno lo hizo a su manera.
Con este panorama, donde cada navegador tenía su propio JavaScript, nació la organización de estándares Ecma International. Se desarrolló el estándar EcmaScript. JavaScript pasaría a ser una implementación de este estándar, con el objetivo de que funcionara igual en cualquier navegador.
El motor de JavaScript
Aunque EcmaScript marque el camino de cómo implementar JavaScript, no dice absolutamente nada sobre cómo los navegadores deben ejecutar el código. Cada navegador tiene su forma (o motor) para ejecutar JavaScript.
Por ejemplo, Netscape (más adelante Mozilla) desarrolló el motor SpiderMonkey. Inicialmente era bastante simple: el código fuente JS se compilaba a Bytecode y luego el intérprete transformaba esto a código binario para poder ser procesado por la CPU. Con el tiempo, este motor ha ido evolucionando para ofrecer un rendimiento mucho más bueno que el que ofrecía inicialmente.
A medida que fueron apareciendo aplicaciones web que requerían de un alto dinamismo e interacción con el usuario, el rendimiento que ofrecían los motores era bastante pobre. Por ello, Google desarrolló el motor V8.
V8 inicialmente añadió algunas novedades en la forma de generar el código binario. Estas novedades fueron un compilador que generaba código binario lo más rápido posible y otro compilador que, mientras la aplicación se estaba ejecutando, entraría en juego para revisar y reemplazar partes del código binario generado por otras más optimizadas. Esta versión de V8 no transformaba el código fuente a Bytecode. Esta evolución vino con el coste de un alto consumo de RAM y CPU. ¿Te suena no?
Posteriormente, V8 sacó una nueva versión que sí transformaba el código fuente en Bytecode. Este código se transformaba en código binario mientras otro compilador optimizaba el mismo y lo iba reemplazando mientras corría la aplicación.
SpiderMonkey a día de hoy funciona de una forma más o menos similar, igual que el motor Chakra de Microsoft (para Internet Explorer). No obstante, el más popular es V8 y está evolucionando constantemente.
Este es el breve resumen, en la siguiente parte, entraremos a fondo en el cómo JavaScript es ejecutado en el navegador.