Wird JavaScript vom Design her interpretiert?

73

Ich bin vorsichtig, diese Frage zu stellen, weil sie übermäßig anspruchsvoll erscheinen könnte. Ich habe gerade JavaScript: The Definitive Guide geöffnet und es heißt auf der ersten Seite von Kapitel 1

"JavaScript ist eine dynamische, untypisierte interpretierte Programmiersprache auf hoher Ebene."

Soll ich also annehmen, dass der interpretierte Teil eine Anforderung in der Sprachspezifikation ist, oder ist es irreführend zu sagen, dass die Sprache eine interpretierte Programmiersprache ist, wenn der Unterschied zwischen einer Sprache und ihren vielen Implementierungen beachtet wird?

Es gibt anscheinend keine statischen Compiler für JavaScript - https://stackoverflow.com/questions/1118138/is-there-a-native-machine-code-compiler-for-javascript. Vielleicht ist es nur ein Spiegelbild davon.

Matt Esch
quelle
Es gab eine Zeit lang ein jscript.net, das AS3 / dem "verlorenen" ES4 ähnlich war. Es wurde Bytecode-kompiliert, um CIL.
Hey
13
V8 behauptet ausdrücklich , kein Interpreter, sondern ein Compiler zu sein.
Pimvdb
@GGG JScript.Net ist noch am Leben und ... krank. Aber noch am Leben. msdn.microsoft.com/en-us/library/72bd815a.aspx
Jetti
1
FWIW, das "untypisierte" Bit ist auch nicht unbedingt wahr
Rob Agar
Firefox hatte soeben den ersten browserbasierten JIT-Compiler in dem Jahr veröffentlicht, in dem diese Frage in FF 3.5 beantwortet wurde. Ich glaube, moderne JITs kompilieren (oder bereiten sich zumindest auf das Kompilieren vor) häufig beim ersten Durchlauf eines JS-Dokuments, um beispielsweise Identifizierungs- und Cache-Methoden auszuführen, die für einen bestimmten Bereich isoliert sind.
Erik Reppen

Antworten:

50

Soll ich also annehmen, dass der interpretierte Teil eine Anforderung in der Sprachspezifikation ist, oder ist es irreführend zu sagen, dass die Sprache eine interpretierte Programmiersprache ist, wenn der Unterschied zwischen einer Sprache und ihren vielen Implementierungen beachtet wird?

EcmaScript-Experten verwenden häufig den Begriff "ES-Interpreter", um sich auf eine Implementierung von EcmaScript zu beziehen, in der Spezifikation wird dieser Begriff jedoch nicht verwendet. Die Sprache im Überblick beschreibt insbesondere die Sprache , in Interpreter unabhängigen Bedingungen:

ECMAScript ist objektbasiert: Grundlegende Sprach- und Hostfunktionen werden von Objekten bereitgestellt, und ein ECMAScript-Programm ist ein Cluster kommunizierender Objekte.

Daher geht EcmaScript von einer "Host-Umgebung" aus, die als Anbieter von Objektdefinitionen definiert ist, einschließlich aller Objekte, die E / A oder andere Links zur Außenwelt zulassen, jedoch keinen Interpreter erfordern.

Die Semantik von Anweisungen und Ausdrücken in der Sprache wird in Form von Vervollständigungsspezifikationen definiert, die in einem Interpreter trivial implementiert sind, die Spezifikation erfordert dies jedoch nicht.

8.9 Der Fertigstellungsspezifikationstyp

Die Fertigstellung Typ wird verwendet , um das Verhalten von Aussagen zu erklären ( break, continue, returnund throw) , die nicht - lokale Transfers von Steuer auszuführen. Werte des Completion - Typs sind Tripel der Form ( Typ , Wert , Ziel ), wobei Typ eines ist normale , bricht , weiterhin , Rückkehr oder werfen , Wert ist jeder ECMAScript Sprache Wert oder leer , und Ziel ist irgendein ECMAScript Identifikator oder leer .

Der Begriff „abrupte Fertigstellung“ bezieht sich auf jede Fertigstellung mit einem anderen als dem normalen Typ .

Die nicht-lokalen Steuerübertragungen können in Anordnungen von Anweisungen mit Sprüngen konvertiert werden, die eine native oder Bytecode-Kompilierung ermöglichen.

"EcmaScript Engine" ist möglicherweise eine bessere Möglichkeit, dieselbe Idee auszudrücken.


Es gibt anscheinend keine statischen Compiler für JavaScript

Das ist nicht wahr. Der "Interpreter" von V8 kompiliert intern in nativen Code, Rhino kompiliert optional intern in Java-Bytecode und verschiedene Mozilla-Interpreter ({Trace, Spider, Jager} Monkey) verwenden einen JIT-Compiler.

V8 :

V8 erhöht die Leistung, indem JavaScript vor der Ausführung in systemeigenen Maschinencode kompiliert wird, anstatt Bytecode auszuführen oder ihn zu interpretieren.

Rhino :

public final void setOptimizationLevel(int optimizationLevel)

Stellen Sie die aktuelle Optimierungsstufe ein. Es wird erwartet, dass die Optimierungsstufe eine ganze Zahl zwischen -1 und 9 ist. Alle negativen Werte werden als -1 interpretiert, und alle Werte über 9 werden als 9 interpretiert. Eine Optimierungsstufe von -1 gibt an, dass der Interpretationsmodus immer aktiv ist benutzt. Die Stufen 0 bis 9 geben an, dass möglicherweise Klassendateien generiert werden. Bei höheren Optimierungsstufen wird die Leistung zur Kompilierungszeit in Relation zur Laufzeitleistung gesetzt. Die Optimierungsstufe kann nicht größer als -1 festgelegt werden, wenn das Optimierungspaket zur Laufzeit nicht vorhanden ist.

TraceMonkey :

TraceMonkey erweitert Mozillas JavaScript®-Engine (bekannt als „SpiderMonkey“) um native Code-Kompilierung. Es basiert auf einer an der UC Irvine entwickelten Technik namens "trace trees" und baut auf Code und Ideen auf, die mit dem Tamarin Tracing-Projekt geteilt wurden. Das Nettoergebnis ist eine massive Geschwindigkeitssteigerung sowohl im Browser-Chrome als auch im Webseiteninhalt.

Mike Samuel
quelle
1
Vielen Dank für diese Antwort, es beantwortet tatsächlich die Frage. Ich nehme an, der letzte Kommentar zu keiner statischen Kompilierung ist der Grund dafür, warum gefragt wurde, welche Implementierungen tatsächlich Code kompilieren und welche nicht. Alles, woran ich interessiert war, war die Gültigkeit der Aussage "JavaScript ist eine interpretierte Sprache", die angesichts von Implementierungszitaten und der fehlenden Definition durch die Spezifikation falsch zu sein scheint. Nicht ermutigend für den zweiten Absatz eines "Definitiven Leitfadens", aber ich denke, ich werde dabei bleiben.
Matt Esch
@ me232, die Aussage war vor 2008 im Wesentlichen wahr. Rhino geht davon aus, dass es sich bei Rhino zwar um einen Hauptinterpreter handelte, und daher hätten nur wenige "den endgültigen Leitfaden" bemängelt, weil sie ihn ignoriert hatten. Ich habe das Buch nicht gelesen und kann daher nicht beurteilen, wie repräsentativ dieser Satz insgesamt ist.
Mike Samuel
Was ist die Definition von "statischen Compiler". Ich dachte, dass Definition bedeutet, dass Kompilierung nur einmal vorkommt und Sie einen statischen (dh unveränderlichen) Bereich von Bits erhalten, den Sie dann ausführen. AFAIK So funktioniert keine JavaScript-Engine. Deshalb haben sie de-optimizationStufen. Mit anderen Worten, JavaScript wird von diesen Engines kompiliert, jedoch nicht statisch.
gman
@gman, Rhinos Bytecode-Generator funktioniert so.
Mike Samuel
AFAIK das ist nicht der Fall. Rhino kann andere JavaScript-Dateien enthalten, die zur Laufzeit kompiliert werden müssen. Das ist keine statische Komplikation.
gman
20

Die in Chrome verwendete V8-JavaScript-VM enthält keinen Interpreter. Stattdessen besteht es aus zwei Compilern und kompiliert den Code im laufenden Betrieb. Einer der Compiler läuft schnell, generiert aber ineffizienten Code, der andere ist ein optimierender Compiler.

Ich kann verstehen, warum manche Leute dieses "Betrügen" in Betracht ziehen, da V8 jedes Mal, wenn der Code ausgeführt wird und der Benutzer V8 installiert haben muss, Quellcode als Eingabe verwendet. Stellen Sie sich aber einen Compiler vor, der eine ausführbare Datei ausgibt, die einen vollständigen Interpreter und Bytecode enthält. Dann hätten Sie ein eigenständiges Programm. Es wäre einfach nicht sehr effizient.

Jørgen Fogh
quelle
19

Das Aufkommen von JIT-Compilern für Skriptsprachen hat die Grenze zwischen Kompilierung und Interpretation verwischt, bis zu einem Punkt, an dem die Frage nicht mehr so ​​viel bedeutet. Ist es nur eine Interpretation, wenn die Engine eine Codezeile liest und diese sofort ausführt? (Shell-Skripte werden normalerweise immer noch auf diese Weise implementiert.) Ist es eine Interpretation, wenn die Engine die gesamte Datei aufnimmt, sie sofort in einen Byte-Code kompiliert und dann den Byte-Code interpretiert? (Die Mozilla-Engine der ersten Stufe und CPython funktionieren auf diese Weise.) Ist es eine Interpretation, wenn die Engine eine Funktion gleichzeitig analysiert und sie mit JIT in nativen Code übersetzt? Was ist mit den Engines, die die gesamte Datei in Byte-Code kompilieren und dann je nach Bedarf eine Funktion nach der anderen ausführen? (Die meisten Skript-Engines funktionieren heutzutage so,

Es gibt viele Schattierungen zwischen Zusammenstellung und Interpretation.

Ich denke, die nützlichste Definition für die Interpretation ist "wird dem Quellcode des Programms zur Ausführungszeit ohne einen separaten Vor-Zeit-Schritt zugeführt". Nach dieser Definition sind alle JavaScript-Engines Interpreter. Dies ist aber sicherlich nicht die einzig mögliche Definition von Interpretation.

Aber ist JavaScript für die Interpretation ausgelegt? In gewisser Weise ja: Es hat eine evalFunktion sowie den FunctionKonstruktor, dass Sie Programmcode als Zeichenfolge angeben können, die ausgeführt wird. Die Fähigkeit zur dynamischen Erstellung von Programmcode zur Laufzeit setzt voraus, dass die Engine in der Lage ist, Quellcode zu interpretieren. Dies bedeutet jedoch nicht, dass Sie nicht alles andere vorab erstellen können. Selbst in einer kompilierten Sprache wie C ++ und C # können Sie Quellcode verwenden, ihn im Speicher in neuen Maschinencode kompilieren und diesen dann ausführen. Es gibt sogar Bibliotheken dafür: LLVM + Clang in C ++ und das Roslyn-Projekt in C #.

Außerdem ist der Übermittlungsmechanismus für JavaScript der Quellcode. Es gibt keine anerkannte Byte-Code-Form davon. C # und Java haben ihren offiziellen Bytecode, und jeder erwartet, dass C ++ als Maschinencode geliefert wird. Dies ist aber nach wie vor kein inhärenter Aspekt der Sprache, sondern nur ein dominantes Nutzungsszenario. Tatsächlich wird JavaScripts enger relativer ActionScript-Code in Flash als Bytecode geliefert (der Flash-Compiler kompiliert alle Skripte vor).

Sebastian Redl
quelle
4

Es gibt keine völlig übereinstimmende Definition von "interpretiert" gegenüber "kompiliert". Bei der klassischen Unterscheidung erzeugen kompilierte Sprachen eine eigenständige ausführbare Binärdatei, während für interpretierte Sprachen eine implementierte Laufzeit zum Ausführen des Codes erforderlich ist. Virtuelle Maschinen, Bytecode usw. verwischen die Unterscheidung.

Aber hier ist eine möglicherweise nützliche Definition: Eine interpretierte Sprache ist eine Sprache, in der die Standardsprache zur Laufzeit Quelltext als Eingabe verwenden und ausführen kann. Durch diese Definition werden Perl-, Python-, Ruby-, JavaScript- und Shell-Skripte und dergleichen interpretiert (auch wenn sie Zwischenschritte wie Bytecode oder sogar systemeigenen Code verwenden). Java, C #, C etc. sind das nicht. Und JavaScript wird per Definition interpretiert, auch wenn die Spezifikation nicht das genaue Wort verwendet.

JacquesB
quelle
Hmm, ich mag es nicht, Java und C in dieselbe Kategorie zu bringen. Vielleicht ist eine bessere Unterscheidung Sprachen, die am häufigsten als (A) Quellcode, (B) Zwischencode oder (C) Maschinencode verbreitet werden. Zum Beispiel A = Javascript, B = Java, C = C.
John Henckel
Das Aufrufen einer Sprache, die entweder interpretiert oder kompiliert ist, ist nicht richtig. Zum Beispiel würden Sie unter dieser Regel zustimmen, dass C ++ eine kompilierte Sprache ist, oder? Was ist dann mit Cling, das C ++ - Code ausführt, ohne ihn zu kompilieren? "und dergleichen werden interpretiert (auch wenn sie Zwischenschritte wie Bytecode oder sogar nativen Code verwenden)" Entsprechend wird auch Java interpretiert, interpretiert von seiner VM.
Abhinav Gauniyal