¿JavaScript se interpreta por diseño?

73

Soy cauteloso de hacer esta pregunta porque puede parecer demasiado exigente. Acabo de abrir JavaScript: la guía definitiva, y dice que en la primera página del capítulo 1

"JavaScript es un lenguaje de programación interpretado de alto nivel, dinámico y sin tipo"

Entonces, ¿debo considerar que la parte interpretada es un requisito en la especificación del lenguaje, o es engañoso decir que el lenguaje es un lenguaje de programación interpretado cuando se respeta la diferencia entre un lenguaje y sus múltiples implementaciones?

Aparentemente no hay compiladores estáticos para JavaScript: https://stackoverflow.com/questions/1118138/is-there-a-native-machine-code-compiler-for-javascript, así que tal vez sea solo un reflejo de esto.

Matt Esch
fuente
Hubo un jscript.net por un tiempo que era similar a AS3 / el ES4 "perdido". Fue compilado por bytecode a CIL.
Hola
13
V8 afirma explícitamente que no es un intérprete sino un compilador.
pimvdb
@GGG JScript.Net sigue vivo y ... enfermo. Pero aún vivo. msdn.microsoft.com/en-us/library/72icsoft15a.aspx
Jetti
1
FWIW, el bit "sin tipo" tampoco es estrictamente cierto
Rob Agar
Firefox acababa de lanzar el primer compilador JIT basado en navegador el año en que la pregunta fue respondida en FF 3.5, por lo que probablemente no se conocía ampliamente en ese momento. Creo que los JIT modernos realmente compilan mucho (o al menos se preparan para compilar) en el primer paso de un documento JS para hacer cosas como identificar y almacenar en caché los métodos que están aislados en un ámbito determinado.
Erik Reppen

Respuestas:

50

Entonces, ¿debo considerar que la parte interpretada es un requisito en la especificación del lenguaje, o es engañoso decir que el lenguaje es un lenguaje de programación interpretado cuando se respeta la diferencia entre un lenguaje y sus múltiples implementaciones?

Los geeks del lenguaje EcmaScript a menudo usan el término "intérprete de ES" para referirse a una implementación de EcmaScript, pero la especificación no usa ese término. El resumen del lenguaje en particular describe el lenguaje en términos independientes del intérprete:

ECMAScript está basado en objetos: los objetos proporcionan lenguaje básico y servicios de host, y un programa ECMAScript es un grupo de objetos en comunicación.

Por lo tanto, EcmaScript asume un "entorno host" que se define como un proveedor de definiciones de objetos, incluidos todos aquellos que permiten E / S o cualquier otro enlace al mundo exterior, pero no requiere un intérprete.

La semántica de las declaraciones y expresiones en el lenguaje se define en términos de especificación de finalización que se implementan trivialmente en un intérprete, pero la especificación no requiere eso.

8.9 El tipo de especificación de finalización

El tipo de finalización se utiliza para explicar el comportamiento de los estados ( break, continue, returny throw) que realizan las transferencias no locales de control. Los valores del tipo de Finalización son triples de la forma ( tipo , valor , objetivo ), donde el tipo es uno de normal , interrumpir , continuar , devolver o arrojar , el valor es cualquier valor del lenguaje ECMAScript o está vacío , y el objetivo es cualquier identificador ECMAScript o vacía .

El término "finalización abrupta" se refiere a cualquier finalización con un tipo diferente al normal .

Las transferencias de control no locales se pueden convertir en matrices de instrucciones con saltos que permiten la compilación nativa o de código de bytes.

"Motor EcmaScript" podría ser una mejor manera de expresar la misma idea.


Aparentemente no hay compiladores estáticos para JavaScript

Esto no es verdad. El "intérprete" V8 compila el código nativo internamente, Rhino compila opcionalmente el código de bytes de Java internamente y varios intérpretes de Mozilla ({Trace, Spider, Jager} Monkey) usan un compilador JIT.

V8 :

V8 aumenta el rendimiento compilando JavaScript en código máquina nativo antes de ejecutarlo, en lugar de ejecutar bytecode o interpretarlo.

Rinoceronte :

public final void setOptimizationLevel(int optimizationLevel)

Establecer el nivel de optimización actual. Se espera que el nivel de optimización sea un número entero entre -1 y 9. Cualquier valor negativo se interpretará como -1, y cualquier valor mayor que 9 se interpretará como 9. Un nivel de optimización de -1 indica que el modo interpretativo siempre será usado. Los niveles del 0 al 9 indican que se pueden generar archivos de clase. Los niveles de optimización más altos compensan el rendimiento en tiempo de compilación por el rendimiento en tiempo de ejecución. El nivel del optimizador no puede establecerse en más de -1 si el paquete del optimizador no existe en tiempo de ejecución.

TraceMonkey :

TraceMonkey agrega compilación de código nativo al motor JavaScript de Mozilla (conocido como "SpiderMonkey"). Se basa en una técnica desarrollada en UC Irvine llamada "árboles traza", y se basa en el código y las ideas compartidas con el proyecto Tamarin Tracing. El resultado neto es un aumento masivo de velocidad tanto en el navegador Chrome como en el contenido de la página web.

Mike Samuel
fuente
1
Gracias por esta respuesta, en realidad responde la pregunta. Supongo que el comentario final sobre la no compilación estática es lo que causó el rumor sobre qué implementaciones compilan realmente el código y cuáles no. Todo lo que me interesaba era la validez de la declaración "JavaScript es un lenguaje interpretado" que, dadas las citas de implementación y la falta de definición por la especificación, parece ser falsa. No es alentador para el segundo párrafo de una "Guía definitiva", pero supongo que me quedaré con ella.
Matt Esch
@ me232, la declaración era sustancialmente cierta antes de 2008. Rhino es anterior a la fecha, pero no era un intérprete importante y muy pocos habrían criticado "la Guía definitiva" en el momento por ignorarla. No he leído el libro, así que no puedo comentar cuán representativa es la oración de su calidad general.
Mike Samuel
¿Cuál es la definición de "compilador estático"? Pensé que la definición significaba que la compilación ocurre solo una vez y obtienes un grupo estático (es decir, inmutable) de bits que luego ejecutas. AFAIK no es así como funciona ningún motor de JavaScript. Por eso tienen de-optimizationpasos. En otras palabras, JavaScript es compilado por estos motores, pero no está compilado estáticamente.
gman
@gman, el generador de bytecode de Rhino funciona de esa manera.
Mike Samuel
AFAIK ese no es el caso. Rhino puede incluir otros archivos JavaScript que deben compilarse en tiempo de ejecución. Eso no es una complicación estática .
Gman
20

La V8 JavaScript VM utilizada en Chrome no incluye un intérprete. En cambio, consta de dos compiladores y compila el código sobre la marcha. Uno de los compiladores se ejecuta rápidamente pero genera código ineficiente, el otro es un compilador optimizador.

Puedo entender por qué algunas personas considerarían esta "trampa", ya que V8 toma el código fuente como entrada cada vez que se ejecuta el código y el usuario debe tener instalado V8. Pero considere un compilador que emite un ejecutable que incluye un intérprete completo y un código de bytes. Entonces tendrías un programa independiente. Simplemente no sería muy eficiente.

Jørgen Fogh
fuente
19

La aparición de compiladores JIT para lenguajes de script ha desdibujado la línea entre la compilación y la interpretación hasta un punto donde la pregunta no significa mucho. ¿Es solo interpretación cuando el motor lee una línea de código y la ejecuta inmediatamente? (Los scripts de Shell todavía se implementan normalmente de esta manera.) ¿Es una interpretación cuando el motor toma todo el archivo, lo compila inmediatamente en algún código de byte y luego interpreta el código de byte? (El motor Mozilla de primera etapa funciona de esta manera, al igual que CPython). ¿Es interpretación cuando el motor analiza una función a la vez y JIT la compila en código nativo? ¿Qué pasa con esos motores que compilan todo el archivo en código de bytes, luego JIT una función a la vez según sea necesario? (La mayoría de los motores de script en estos días funcionan de esta manera,

Hay muchos matices entre la compilación y la interpretación.

Creo que la definición más útil para la interpretación es "se alimenta el código fuente del programa en el momento de la ejecución, sin un paso por adelantado por separado". Según esta definición, todos los motores de JavaScript son intérpretes. Pero esta ciertamente no es la única definición posible de interpretación.

¿Pero está diseñado JavaScript para la interpretación? En cierto modo, sí: tiene una evalfunción, así como el Functionconstructor, que puede dar código de programa como una cadena que se ejecutará. La capacidad de construir dinámicamente el código del programa en tiempo de ejecución requiere que el motor sea capaz de interpretar el código fuente. Pero esto no significa que no pueda hacer todo lo demás con anticipación. Incluso en un lenguaje compilado como C ++ y C #, puede tomar el código fuente, compilarlo en la memoria en un nuevo código de máquina y luego ejecutarlo. Incluso hay bibliotecas para eso: LLVM + Clang en C ++ y el proyecto Roslyn en C #.

Además, el mecanismo de entrega de JavaScript es el código fuente; no hay una forma de código de byte reconocida. C # y Java tienen su código de byte oficial, y todos esperan que C ++ se entregue como código de máquina. Pero este aún no es un aspecto inherente al lenguaje, solo un escenario de uso dominante. De hecho, el cercano ActionScript en Flash de JavaScript se entrega como código de bytes (el compilador de Flash precompila todos los scripts).

Sebastian Redl
fuente
4

No existe una definición totalmente acordada de 'interpretado' versus 'compilado'. En la distinción clásica, los lenguajes compilados producen un ejecutable binario independiente, mientras que los lenguajes interpretados requieren un tiempo de ejecución desplegado para ejecutar el código. Las máquinas virtuales, el código de bytes, etc., borra la distinción.

Pero aquí hay una definición posiblemente útil: un lenguaje interpretado es un lenguaje en el que el tiempo de ejecución del lenguaje estándar puede tomar el texto del código fuente como entrada y ejecutarlo. Según esa definición, se interpretan Perl, Python, Ruby, JavaScript y scripts de shell y similares (incluso si usan pasos intermedios como bytecode o incluso código nativo). Java, C #, C etc. no lo son. Y JavaScript se interpreta por definición, incluso si la especificación no usa la palabra exacta.

JacquesB
fuente
Hmm, no me gusta poner Java y C en la misma categoría. Quizás una mejor distinción son los lenguajes que se distribuyen más comúnmente como (A) código fuente, (B) código intermedio o (C) código de máquina. Por ejemplo, A = javascript, B = Java, C = C.
John Henckel
Llamar a un idioma interpretado o compilado no está bien. Por ejemplo, bajo esa regla, estaría de acuerdo en que C ++ es un lenguaje compilado, ¿verdad? Entonces, ¿qué pasa con Cling, que ejecuta código c ++ sin compilarlo? "y similares se interpretan (incluso si utilizan pasos intermedios como bytecode o incluso código nativo)" De acuerdo con esto, Java también se interpreta, interpretado por su VM.
Abhinav Gauniyal