JavaScript è interpretato dal design?


73

Sono cauto nel porre questa domanda perché potrebbe apparire eccessivamente complicata. Ho appena aperto JavaScript: The Definitive Guide, e afferma la prima pagina del capitolo 1

"JavaScript è un linguaggio di programmazione interpretato di alto livello, dinamico e non tipizzato”

Quindi devo ritenere che la parte interpretata sia un requisito nelle specifiche del linguaggio, o è fuorviante dire che il linguaggio è un linguaggio di programmazione interpretato nel rispetto della differenza tra un linguaggio e le sue numerose implementazioni?

Apparentemente non ci sono compilatori statici per JavaScript - https://stackoverflow.com/questions/1118138/is-there-a-native-machine-code-compiler-for-javascript, quindi forse è solo un riflesso di questo.


Per un po 'c'è stato un jscript.net simile a AS3 / ES4 "perso". È stato compilato bytecode per CIL.
Ehi,

13
V8 afferma esplicitamente di non essere un interprete ma un compilatore.
pimvdb,

@GGG JScript.Net è ancora vivo e ... malato. Ma ancora vivo. msdn.microsoft.com/en-us/library/72bd815a.aspx
Jetti

1
FWIW, il bit "non tipizzato" non è neppure vero
Rob Agar

Firefox aveva appena rilasciato il primo compilatore JIT basato su browser l'anno in cui a FF 3.5 era stata data risposta a una domanda, quindi probabilmente all'epoca non era ampiamente conosciuta. Credo che le moderne JIT realizzino molto (o almeno prep per la compilazione) al primo passaggio di un documento JS per fare cose come identificare e memorizzare nella cache metodi che sono isolati in un determinato ambito.
Erik Reppen,

Risposte:


50

Quindi devo ritenere che la parte interpretata sia un requisito nelle specifiche del linguaggio, o è fuorviante dire che il linguaggio è un linguaggio di programmazione interpretato nel rispetto della differenza tra un linguaggio e le sue numerose implementazioni?

I fanatici del linguaggio EcmaScript usano spesso il termine "interprete ES" per fare riferimento a un'implementazione di EcmaScript, ma le specifiche non usano quel termine. La panoramica della lingua in particolare descrive la lingua in termini di interprete-agnostico:

ECMAScript è basato su oggetti: il linguaggio di base e le strutture host sono fornite da oggetti e un programma ECMAScript è un cluster di oggetti comunicanti.

Quindi EcmaScript presuppone un "ambiente host" che viene definito come fornitore di definizioni di oggetti, compresi tutti quelli che consentono I / O o qualsiasi altro collegamento con il mondo esterno, ma non richiede un interprete.

La semantica delle dichiarazioni e delle espressioni nel linguaggio è definita in termini di specifiche di completamento che sono banalmente implementate in un interprete, ma le specifiche non lo richiedono.

8.9 Il tipo di specifica di completamento

Il tipo di completamento è utilizzato per spiegare il comportamento di istruzioni ( break, continue, returne throw) che eseguono trasferimenti non locali di controllo. I valori del tipo Completamento sono triple del modulo ( tipo , valore , destinazione ), dove il tipo è normale , interrompi , continua , restituisce o genera , il valore è qualsiasi valore del linguaggio ECMAScript o vuoto e il target è qualsiasi identificatore ECMAScript o vuoto .

Il termine "completamento improvviso" si riferisce a qualsiasi completamento con un tipo diverso dal normale .

I trasferimenti di controllo non locali possono essere convertiti in matrici di istruzioni con salti che consentono la compilazione nativa o con codice byte.

"EcmaScript Engine" potrebbe essere un modo migliore per esprimere la stessa idea.


Apparentemente non ci sono compilatori statici per JavaScript

Questo non è vero. L '"interprete" V8 compila internamente il codice nativo, Rhino facoltativamente compila internamente il bytecode Java e vari interpreti Mozilla ({Trace, Spider, Jager} Monkey) usano un compilatore JIT.

V8 :

V8 aumenta le prestazioni compilando JavaScript nel codice macchina nativo prima di eseguirlo, anziché eseguire bytecode o interpretarlo.

Rhino :

public final void setOptimizationLevel(int optimizationLevel)

Imposta il livello di ottimizzazione corrente. Il livello di ottimizzazione dovrebbe essere un numero intero compreso tra -1 e 9. Qualsiasi valore negativo verrà interpretato come -1 e qualsiasi valore maggiore di 9 verrà interpretato come 9. Un livello di ottimizzazione -1 indica che la modalità interpretativa sarà sempre Usato. I livelli da 0 a 9 indicano che è possibile generare file di classe. Livelli di ottimizzazione più elevati compromettono le prestazioni in fase di compilazione per le prestazioni di runtime. Il livello di ottimizzatore non può essere impostato maggiore di -1 se il pacchetto di ottimizzatore non esiste in fase di esecuzione.

TraceMonkey :

TraceMonkey aggiunge la compilazione in codice nativo al motore JavaScript® di Mozilla (noto come "SpiderMonkey"). Si basa su una tecnica sviluppata in UC Irvine chiamata "trace alberi" e basata su codice e idee condivisi con il progetto Tamarin Tracing. Il risultato netto è un enorme aumento della velocità sia nel contenuto del browser che nel contenuto della pagina Web.


1
Grazie per questa risposta, in realtà risponde alla domanda. Suppongo che il commento finale su nessuna compilazione statica sia ciò che ha causato il buzz su quali implementazioni effettivamente compilano il codice e quali no. Tutto quello che mi interessava era la validità della frase "JavaScript è un linguaggio interpretato" che, date le citazioni di implementazione e la mancanza di definizione da parte delle specifiche, sembra essere falso. Non incoraggiante per il secondo paragrafo di una "Guida definitiva", ma credo che mi atterrò.
Matt Esch,

@ me232, l'affermazione era sostanzialmente vera prima del 2008. Rhino ha una data precedente, ma non era un interprete importante e così pochi avrebbero criticato la "Guida definitiva" per ignorarla. Non ho letto il libro, quindi non posso commentare quanto sia rappresentativa quella frase della sua qualità generale.
Mike Samuel,

Qual è la definizione di "compilatore statico". Pensavo che la definizione significasse che la compilazione avviene una sola volta e ottieni un bucket statico (cioè immutabile) di bit che poi esegui. AFAIK, non è così che funziona un motore JavaScript. Ecco perché hanno dei de-optimizationpassaggi. In altre parole, JavaScript viene compilato da questi motori ma non viene compilato staticamente.
Gman,

@gman, il generatore di bytecode di Rhino funziona in questo modo.
Mike Samuel,

AFAIK non è così. Rhino può includere altri file JavaScript che devono essere compilati in fase di esecuzione. Questa non è una complicazione statica .
Gman,

20

La VM JavaScript V8 utilizzata in Chrome non include un interprete. Invece è composto da due compilatori e compila il codice al volo. Uno dei compilatori viene eseguito rapidamente ma genera codice inefficiente, l'altro è un compilatore ottimizzato.

Posso capire perché alcune persone considererebbero questo "imbroglio", poiché V8 prende il codice sorgente come input ogni volta che il codice viene eseguito e l'utente deve avere V8 installato. Ma considera un compilatore che emette un eseguibile che include un interprete e un bytecode completi. Quindi avresti un programma autonomo. Semplicemente non sarebbe molto efficiente.


19

L'emergere di compilatori JIT per i linguaggi di script ha offuscato il confine tra compilazione e interpretazione a un punto in cui la domanda non significa molto. È solo interpretazione quando il motore legge una riga di codice e la esegue immediatamente? (Gli script della shell sono ancora generalmente implementati in questo modo.) È interpretazione quando il motore prende l'intero file, lo compila immediatamente in qualche codice byte e quindi interpreta il codice byte? (Il motore Mozilla del primo stadio funziona in questo modo, così come CPython.) È interpretazione quando il motore analizza una funzione alla volta e JIT la compila in codice nativo? Che dire di quei motori che compilano l'intero file in codice byte, quindi JIT una funzione alla volta secondo necessità? (La maggior parte dei motori di script attualmente funziona in questo modo,

Ci sono molte sfumature tra la compilazione e l'interpretazione.

Penso che la definizione più utile per l'interpretazione sia "viene alimentato il codice sorgente del programma in fase di esecuzione, senza una fase separata anticipata". Con questa definizione, tutti i motori JavaScript sono interpreti. Ma questa non è certamente l'unica possibile definizione di interpretazione.

Ma JavaScript è progettato per l'interpretazione? In un certo senso, sì: ha una evalfunzione e il Functioncostruttore che è possibile fornire il codice del programma come una stringa che verrà eseguita. La capacità di costruire dinamicamente il codice del programma in fase di esecuzione richiede che il motore sia in grado di interpretare il codice sorgente. Ma questo non significa che non puoi fare tutto il resto in anticipo. Anche in un linguaggio compilato come C ++ e C # è possibile prendere il codice sorgente, compilarlo in memoria nel nuovo codice macchina e quindi eseguirlo. Ci sono anche librerie per questo: LLVM + Clang in C ++ e il progetto Roslyn in C #.

Inoltre, il meccanismo di consegna per JavaScript è il codice sorgente; non esiste una forma di codice byte riconosciuta. C # e Java hanno il loro codice byte ufficiale e tutti si aspettano che C ++ venga consegnato come codice macchina. Ma questo non è ancora un aspetto intrinseco se la lingua, solo uno scenario di utilizzo dominante. In effetti, il codice ActionScript relativo relativo di JavaScript in Flash viene effettivamente distribuito come codice byte (il compilatore Flash precompila tutti gli script).


4

Non esiste una definizione totalmente concordata di "interpretato" rispetto a "compilato". Nella distinzione classica, i linguaggi compilati producono un eseguibile binario autonomo, mentre i linguaggi interpretati richiedono un runtime distribuito per eseguire il codice. Macchine virtuali, bytecode e così via offusca la distinzione.

Ma ecco una definizione forse utile: una lingua interpretata è una lingua in cui il runtime di lingua standard è in grado di prendere il testo del codice sorgente come input ed eseguirlo. Con tale definizione vengono interpretati gli script Perl, Python, Ruby, JavaScript e shell e simili (anche se usano passaggi intermedi come bytecode o persino codice nativo). Java, C #, C ecc. Non lo sono. E JavaScript è per definizione interpretato, anche se le specifiche non usano la parola esatta.


Hmm, non mi piace mettere Java e C nella stessa categoria. Forse una distinzione migliore sono le lingue che sono più comunemente distribuite come (A) codice sorgente, (B) codice intermedio o (C) codice macchina. Ad esempio A = javascript, B = Java, C = C.
John Henckel,

Chiamare una lingua interpretata o compilata non è giusto. Ad esempio, in base a tale regola, accetteresti che C ++ sia un linguaggio compilato, giusto? E che dire di Cling, che esegue il codice c ++ senza compilarlo. "e simili vengono interpretati (anche se usano passaggi intermedi come bytecode o persino codice nativo)" Acc, anche java viene interpretato, interpretato dalla sua VM.
Abhinav Gauniyal,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.