Czy JavaScript jest interpretowany projektowo?

73

Ostrożnie zadaję to pytanie, ponieważ może wydawać się zbyt wybredne. Właśnie otworzyłem JavaScript: The Definitive Guide i zawiera on pierwszą stronę rozdziału 1

„JavaScript jest dynamicznym, dynamicznym, bez typowania interpretowanym językiem programowania na wysokim poziomie”

Czy mam zatem przyjąć, że interpretowana część jest wymogiem w specyfikacji języka, czy też mylące jest twierdzenie, że język jest interpretowanym językiem programowania, jeśli chodzi o różnicę między językiem a jego wieloma implementacjami?

Najwyraźniej nie ma statycznych kompilatorów dla JavaScript - https://stackoverflow.com/questions/1118138/is-there-a-native-machine-code-compiler-for-javascript, więc może to tylko odzwierciedlenie tego.

Matt Esch
źródło
Przez pewien czas istniało jscript.net, które było podobne do AS3 / „zagubionego” ES4. Został skompilowany bajtowo do CIL.
Hej,
13
Wersja 8 wyraźnie twierdzi, że nie jest tłumaczem, ale kompilatorem.
pimvdb,
@GGG JScript.Net wciąż żyje i ... choruje. Ale wciąż żyje. msdn.microsoft.com/en-us/library/72bd815a.aspx
Jetti
1
FWIW, bit „bez typu” również nie jest do końca prawdą
Rob Agar
Firefox wydał właśnie pierwszy kompilator JIT oparty na przeglądarce w roku, w którym na FF 3.5 udzielono odpowiedzi na to pytanie, więc prawdopodobnie nie był wtedy szeroko znany. Wierzę, że współczesne JIT faktycznie wykonują dużo kompilacji (lub przynajmniej przygotowują się do kompilacji) przy pierwszym przejściu dokumentu JS, aby wykonywać takie czynności, jak identyfikacja i buforowanie metod, które są izolowane w danym zakresie.
Erik Reppen

Odpowiedzi:

50

Czy mam zatem przyjąć, że interpretowana część jest wymogiem w specyfikacji języka, czy też mylące jest twierdzenie, że język jest interpretowanym językiem programowania, jeśli chodzi o różnicę między językiem a jego wieloma implementacjami?

Maniacy języka EcmaScript często używają terminu „tłumacz ES” w odniesieniu do implementacji EcmaScript, ale specyfikacja nie używa tego terminu. Przegląd język w szczególności opisuje język w kategoriach tłumacza-agnostyk:

ECMAScript jest oparty na obiektach: podstawowe funkcje języka i hosta są zapewniane przez obiekty, a program ECMAScript to klaster komunikujących się obiektów.

Tak więc EcmaScript zakłada „środowisko hosta”, które jest zdefiniowane jako dostawca definicji obiektów, w tym wszystkie te, które zezwalają na operacje we / wy lub jakiekolwiek inne łącza do świata zewnętrznego, ale nie wymagają interpretera.

Semantyka instrukcji i wyrażeń w języku jest zdefiniowana w kategoriach specyfikacji uzupełniania, które są trywialnie zaimplementowane w interpreterie, ale specyfikacja tego nie wymaga.

8.9 Typ specyfikacji ukończenia

Rodzaj budowy służy do wyjaśnienia zachowania sprawozdania ( break, continue, returni throw), które wykonują nielokalnych transfery kontroli. Wartości typu Completion to trzykrotki formy ( typ , wartość , cel ), gdzie typ jest jednym z normalnych , break , kontynuuj , zwróć lub wyrzuć , wartość jest dowolną wartością języka ECMAScript lub pustą , a celem jest dowolny identyfikator ECMAScript lub pusty .

Termin „nagłe ukończenie” odnosi się do każdego uzupełnienia innego rodzaju niż normalny .

Nielokalne przekazywanie kontroli można przekształcić w tablice instrukcji ze skokami pozwalającymi na kompilację kodu natywnego lub bajtowego.

„EcmaScript Engine” może być lepszym sposobem wyrażenia tego samego pomysłu.


Najwyraźniej nie ma statycznych kompilatorów dla JavaScript

To nie jest prawda. „Interpreter” V8 kompiluje wewnętrznie kod macierzysty, Rhino opcjonalnie kompiluje wewnętrznie kod bajtowy Java, a różne interpretery Mozilli ({Trace, Spider, Jager} Monkey) używają kompilatora JIT.

V8 :

Wersja 8 zwiększa wydajność, kompilując JavaScript do natywnego kodu maszynowego przed jego uruchomieniem, w porównaniu do wykonania kodu bajtowego lub jego interpretacji.

Nosorożec :

public final void setOptimizationLevel(int optimizationLevel)

Ustaw bieżący poziom optymalizacji. Oczekuje się, że poziom optymalizacji będzie liczbą całkowitą od -1 do 9. Wszelkie wartości ujemne będą interpretowane jako -1, a wszelkie wartości większe niż 9 będą interpretowane jako 9. Poziom optymalizacji -1 wskazuje, że tryb interpretacji zawsze będzie używany. Poziomy od 0 do 9 wskazują, że można wygenerować pliki klas. Wyższe poziomy optymalizacji zmniejszają wydajność czasu kompilacji dla wydajności środowiska wykonawczego. Poziomu optymalizatora nie można ustawić na wartość wyższą niż -1, jeśli pakiet optymalizatora nie istnieje w czasie wykonywania.

TraceMonkey :

TraceMonkey dodaje kompilację kodu natywnego do silnika JavaScript® Mozilli (znanego jako „SpiderMonkey”). Opiera się na technice opracowanej w UC Irvine o nazwie „drzewa śledzące” oraz w oparciu o kod i idee wspólne dla projektu Tamarin Tracing. Wynik netto to ogromny wzrost prędkości zarówno w przeglądarce Chrome, jak i zawartości strony internetowej.

Mike Samuel
źródło
1
Dzięki za tę odpowiedź, tak naprawdę odpowiada na pytanie. Przypuszczam, że ostatni komentarz na temat braku kompilacji statycznej spowodował szum o tym, które implementacje faktycznie kompilują kod, a które nie. Interesowało mnie tylko ważność stwierdzenia „JavaScript jest językiem interpretowanym”, który, biorąc pod uwagę cytaty z implementacji i brak definicji specyfikacji, wydaje się fałszywy. Nie zachęca do drugiego akapitu „Przewodnika ostatecznego”, ale chyba się go trzymam.
Matt Esch,
@ me232, stwierdzenie to było zasadniczo prawdziwe przed 2008 r. Rhino umawiał się wcześniej, ale nie był głównym tłumaczem, a więc niewielu z nich w tym czasie zignorowało „Przewodnik ostateczny” za zignorowanie go. Nie przeczytałem książki, więc nie mogę wypowiedzieć się na temat reprezentatywności tego zdania w jego ogólnej jakości.
Mike Samuel
Jaka jest definicja „kompilatora statycznego”. Myślałem, że ta definicja oznacza, że ​​kompilacja odbywa się tylko raz i otrzymujesz statyczny (tj. Niezmienny) zestaw bitów, który następnie wykonujesz. AFAIK to nie działa żaden silnik JavaScript. Dlatego mają de-optimizationkroki. Innymi słowy JavaScript jest kompilowany przez te silniki, ale nie jest kompilowany statycznie.
gman
@gman, generator bajtów Rhino działa w ten sposób.
Mike Samuel
AFAIK tak nie jest. Rhino może zawierać inne pliki JavaScript, które należy skompilować w czasie wykonywania. To nie jest komplikacja statyczna .
G-Man
20

Wirtualna maszyna wirtualna JavaScript V8 używana w Chrome nie zawiera interpretera. Zamiast tego składa się z dwóch kompilatorów i kompiluje kod w locie. Jeden z kompilatorów działa szybko, ale generuje nieefektywny kod, drugi to kompilator optymalizujący.

Rozumiem, dlaczego niektórzy ludzie uważają to „oszukiwanie”, ponieważ V8 pobiera kod źródłowy jako dane wejściowe za każdym razem, gdy kod jest uruchamiany, a użytkownik musi zainstalować V8. Ale rozważ kompilator, który emituje plik wykonywalny, który zawiera kompletny interpreter i kod bajtowy. Wtedy miałbyś samodzielny program. To po prostu nie byłoby bardzo wydajne.

Jørgen Fogh
źródło
19

Pojawienie się kompilatorów JIT dla języków skryptowych zatarło granicę między kompilacją a interpretacją do tego stopnia, że ​​pytanie nie znaczy aż tyle. Czy to tylko interpretacja, gdy silnik odczytuje wiersz kodu i natychmiast go wykonuje? (Skrypty powłoki są zwykle zwykle implementowane w ten sposób.) Czy to interpretacja, gdy silnik pobiera cały plik, natychmiast kompiluje go do jakiegoś kodu bajtowego, a następnie interpretuje kod bajtowy? (Pierwszy etap silnika Mozilli działa w ten sposób, podobnie jak CPython.) Czy to interpretacja, gdy silnik analizuje funkcję na raz i JIT kompiluje ją do kodu natywnego? Co z tymi silnikami, które kompilują cały plik do kodu bajtowego, a następnie JIT w razie potrzeby jedną funkcję na raz? (Obecnie większość silników skryptowych działa w ten sposób,

Istnieje wiele odcieni między kompilacją a interpretacją.

Myślę, że najbardziej użyteczną definicją interpretacji jest „podaje kod źródłowy programu w czasie wykonywania, bez osobnego kroku wyprzedzającego”. Zgodnie z tą definicją wszystkie silniki JavaScript są tłumaczami. Ale z pewnością nie jest to jedyna możliwa definicja interpretacji.

Ale czy JavaScript jest przeznaczony do interpretacji? W pewnym sensie tak: ma zarówno evalfunkcję, jak i Functionkonstruktor, że możesz podać kod programu jako ciąg znaków, który zostanie wykonany. Zdolność do dynamicznego konstruowania kodu programu w czasie wykonywania wymaga, aby silnik mógł interpretować kod źródłowy. Ale to nie znaczy, że nie da się zrobić wszystkiego z wyprzedzeniem. Nawet w skompilowanym języku, takim jak C ++ i C #, możesz pobrać kod źródłowy, skompilować go w pamięci do nowego kodu maszynowego, a następnie go uruchomić. Istnieją nawet biblioteki: LLVM + Clang w C ++ i projekt Roslyn w C #.

Ponadto mechanizmem dostarczania kodu JavaScript jest kod źródłowy; nie ma rozpoznanej formy kodu bajtowego. C # i Java mają swój oficjalny kod bajtowy i wszyscy oczekują, że C ++ będzie dostarczany jako kod maszynowy. Ale to wciąż nie jest nieodłącznym aspektem, jeśli język, tylko dominujący scenariusz użycia. W rzeczywistości bliski krewny ActionScript JavaScript we Flashu jest w rzeczywistości dostarczany jako kod bajtowy (kompilator Flash wstępnie kompiluje wszystkie skrypty).

Sebastian Redl
źródło
4

Nie ma całkowicie uzgodnionej definicji „interpretowane” a „skompilowane”. W klasycznym rozróżnieniu języki skompilowane tworzą autonomiczny binarny plik wykonywalny, podczas gdy języki interpretowane wymagają wdrożonego środowiska wykonawczego do wykonania kodu. Maszyny wirtualne, kod bajtowy itd. Zacierają rozróżnienie.

Ale oto prawdopodobnie przydatna definicja: język interpretowany to język, w którym standardowy język wykonawczy języka może pobierać tekst kodu źródłowego jako dane wejściowe i go wykonywać. Zgodnie z tą definicją interpretuje się skrypty Perl, Python, Ruby, JavaScript i powłoki itp. (Nawet jeśli używają pośrednich kroków, takich jak kod bajtowy lub nawet kod macierzysty). Java, C #, C itp. Nie są. JavaScript jest z definicji interpretowany, nawet jeśli specyfikacja nie używa dokładnego słowa.

JacquesB
źródło
Hmm, nie lubię umieszczać Java i C w tej samej kategorii. Być może lepszym rozróżnieniem są języki, które są najczęściej rozpowszechniane jako (A) kod źródłowy, (B) kod pośredni lub (C) kod maszynowy. Na przykład A = javascript, B = Java, C = C.
John Henckel,
Nazywanie języka interpretowanym lub kompilowanym jest niewłaściwe. Na przykład zgodnie z tą zasadą zgodziłbyś się, że C ++ jest językiem kompilowanym, prawda? A co z Clingiem, który wykonuje kod c ++ bez jego kompilacji. „i tym podobne są interpretowane (nawet jeśli używają pośrednich kroków, takich jak kod bajtowy lub nawet kod macierzysty)”. W związku z tym Java jest również interpretowana, interpretowana przez jej maszynę wirtualną.
Abhinav Gauniyal,