Nashorn: JavaScript wurde in Java 8 großartig gemacht

Nashorn, ausgesprochen "nass-horn", ist deutsch für "Nashorn" und einer der Tiernamen für einen deutschen Panzer-Zerstörer, der im Zweiten Weltkrieg verwendet wurde. Es ist auch der Name des mit Java 8 eingeführten Ersatzes für die alte, langsame Rhino JavaScript-Engine. Sowohl Rhino als auch Nashorn sind Implementierungen der JavaScript-Sprache, die für die Ausführung auf der Java Virtual Machine (JVM) geschrieben wurde.

Obligatorische Rant: JavaScript hat möglicherweise Java als Teil seines Namens, aber die beiden Sprachen unterscheiden sich sehr in Geist und Design sowie in ihren Implementierungen. Eine Möglichkeit, einen JavaScript-Interpreter zu implementieren, besteht darin, JavaScript in Java-Bytecodes zu kompilieren, wofür Rhino und Nashorn entwickelt wurden.

Sie denken wahrscheinlich an JavaScript, wenn Sie Webbrowser als Skripte verwenden, und Sie hätten größtenteils Recht. Es wird auch für Server verwendet. Mit Node.js werden beispielsweise schnelle, leichtgewichtige Server erstellt, die auf der V8-JavaScript-Engine von Google Chrome basieren. JavaScript-Engines in Webbrowsern haben Zugriff auf das HTML-Dokumentobjektmodell (DOM) und können HTML-Elemente über das DOM bearbeiten. Da verschiedene Webbrowser unterschiedliche DOMs und JavaScript-Engines haben, versuchen Frameworks wie jQuery, die Implementierungsdetails vor dem Programmierer zu verbergen.

Nashorn und Rhino davor unterstützen das Browser-DOM ausdrücklich nicht. In der JVM implementiert, werden sie normalerweise für Endbenutzerskripte in Java-Anwendungen aufgerufen. Nashorn und Rhino können in Java-Programme eingebettet und als Befehlszeilen-Shells verwendet werden. Die zusätzliche Magie, die beim Skripten von Java aus JavaScript benötigt wird, besteht natürlich darin, die Daten- und Typinkongruenzen zwischen den beiden Sprachen zu überbrücken.

Probleme mit Rhino

Die Entwicklung von Nashörnern begann 1997 bei Netscape für ein unglückliches "Javagator" -Projekt und wurde 1998 auf Mozilla.org veröffentlicht. Anschließend wurde sie an Sun und andere lizenziert. Ehrlich gesagt könnte 1998 genauso gut die Jurazeit sein, wie die Internetentwicklung zeigt - 16 Jahre später hat Rhino sein Alter deutlich gezeigt. Laut Jim Laskey von Oracle, dem Hauptentwickler von Nashorn:

Ich bin mir sicher, dass dies alles wahr ist, aber als abgestumpfter Entwickler und Entwicklungsmanager finde ich den letzten Satz sehr amüsant. Große Umschreibungen machen schließlich nie Spaß. Von vorne anfangen macht immer Spaß.

Ziele von Nashorn

Laskey beschrieb seine Ziele für Nashorn wie folgt:

  • Nashorn basiert auf der Sprachspezifikation ECMAScript-262 Edition 5.1 und muss die ECMAScript-262-Konformitätstests bestehen.
  • Nashorn wird die javax.script(JSR 223) API unterstützen.
  • Es wird Unterstützung für das Aufrufen von Java-Code aus JavaScript und für das Aufrufen von JavaScript-Code durch Java bereitgestellt. Dies beinhaltet die direkte Zuordnung zu JavaBeans.
  • Nashorn wird ein neues Befehlszeilentool definieren jjs, mit dem JavaScript-Code in "shebang" -Skripten ausgewertet, hier dokumentiert und Zeichenfolgen bearbeitet werden können.
  • Leistung und Speichernutzung von Nashorn-Anwendungen sollten deutlich besser sein als bei Rhino.
  • Nashorn wird keine zusätzlichen Sicherheitsrisiken aussetzen.
  • Mitgelieferte Bibliotheken sollten unter Lokalisierung korrekt funktionieren.
  • Fehlermeldungen und Dokumentationen werden internationalisiert.

Laskey beschränkte den Umfang des Projekts auch ausdrücklich auf einige "Nichtziele":

  • Nashorn unterstützt nur ECMAScript-262 Edition 5.1. Es werden keine Funktionen der Edition 6 oder nicht standardmäßige Funktionen anderer JavaScript-Implementierungen unterstützt.
  • Nashorn wird keine Browser-Plug-In-API enthalten.
  • Nashorn bietet keine Unterstützung für DOM / CSS oder verwandte Bibliotheken (wie jQuery, Prototype oder Dojo).
  • Nashorn bietet keine Unterstützung für direktes Debuggen.

Was bedeutet es also, auf ECMAScript-262 Edition 5.1 zu basieren? Das Unterscheidungsmerkmal hierbei ist, dass Rhino auf der älteren, weniger leistungsfähigen Edition 3 basiert. Die javax.script(JSR 223) -API dient zum Aufrufen von JavaScript aus Java.

Der Mangel an Debugging-Unterstützung in Nashorn ist ein Rückschritt gegenüber Rhino, das über einen eigenen JavaScript-Debugger verfügt. In mindestens zwei gängigen IDEs finden Sie jedoch Problemumgehungen für dieses absichtliche Auslassen.

Nashorn-Befehlszeilentools: Installieren von jjs und jrunscript

Nachdem ich über Nashorns Befehlszeilentool gelesen hatte jjs, wollte ich unbedingt die Shell auf meinem iMac ausprobieren, aber nach der Installation von Java 8 war sie für die Bash-Shell nicht verfügbar. Es stellte sich heraus, dass die Dokumentation und Implementierung nicht vollständig synchron waren.

Ich wusste, dass die Installation erfolgreich war:

 >java -version java version "1.8.0" Java(TM) SE Runtime Environment (build 1.8.0-b132) Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode) 

aber das Laufen jjskehrte zurück -bash: jjs: command not found. Ein kleines Stöbern brachte mich in das /usr/bin/Verzeichnis:

 >which java /usr/bin/java 

Dort fand ich etwas namens jrunscript, das sich als eine Variante herausstellte jjs, die ein zusätzliches Startskript ausführt . Das hätte mich zufrieden stellen sollen, aber ich war verwirrt darüber, warum das dokumentierte jjsTool nicht /usr/bin/mit der restlichen Java 8-Laufzeit installiert wurde . JavaVirtualMachinesNach ein wenig Recherche habe ich mir die Installation für Java 8 angesehen. Suchen Sie auf einem Mac nach jjsin /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/bin/oder /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/bin/.

Sie können einen Alias ​​für jjsim letzteren Verzeichnis definieren und zu Ihrer Shell-Konfiguration hinzufügen, wenn Sie ihn für die Skripterstellung auf einem Mac oder Linux benötigen. Auf einem PC können Sie jre/bin/Ihrem Verzeichnis das richtige Verzeichnis hinzufügen PATH. In seinem Video vom Start von Java 8 schlägt Jim Laskey vor, jjsin das /usr/bin/Verzeichnis zu kopieren , aber als ich das tat, stellte ich fest, dass jjsdie JRE zur Laufzeit nicht richtig gefunden werden konnte.

Ausführen von JavaScript-Skripten

Warum die beiden Befehlszeilentools zum Ausführen von JavaScript-Skripten? Mir ist nicht ganz klar, was das Entwicklungsteam gedacht hat, aber es jjsverfügt über Funktionen, die jrunscriptdies nicht jrunscripttun , und über eine Initialisierungsdatei. Im Folgenden finden Sie einige einfache Beispiele jjsund jrunscriptVerwendungsmöglichkeiten.

 $ jrunscript nashorn> alert("hello, "); script error: ReferenceError: "alert" is not defined in  at line number 1 

Dies funktioniert nicht, da alert()es sich um eine Browser- / DOM-Funktion handelt. D'oh! Ich hätte schwören können, dass das in Rhino funktioniert hat.

 nashorn> print("Hello, "); Hello,  

Dies funktioniert, da print () eine JavaScript-Kernfunktion ist.

 nashorn> var a = 1; nashorn> var b = "1"; nashorn> print (a+b); 11 nashorn> print(a+a); 2 nashorn> quit(); $ 

Mit anderen Worten, wir haben hier eine grundlegende REPL-Umgebung (Read-Execute-Print-Loop-Befehlszeile) für JavaScript. Wenn Sie von der Antwort auf überrascht sind a+b, beachten Sie Folgendes :

 nashorn> print (typeof(a+b)); string 

Dies ist ein reizvoller Nebeneffekt der losen Eingabe und Überladung des Operators "+" in JavaScript. Es ist korrektes Verhalten gemäß der JavaScript-Spezifikation, kein Fehler.

Nashorn supports the "#" character as a leading line comment marker, so jjs and jrunscript can be used in executable "shebang" scripts written in JavaScript. On a Mac or Linux, you'll have to mark the JavaScript file as executable with the chmod utility to make it runnable.

You'll find a scripting mode in jjs that jrunscript seems to lack. In scripting mode, expressions inside back-ticks are passed to the outer shell for evaluation:

 $ jjs -scripting jjs> print ('ls'); Applications Applications (Parallels) Creative Cloud Files Desktop ... work jjs>

Scripting mode also enables an extension for "heredocs," which are basically multiline strings in a format familiar to Perl and Ruby programmers.

By the way, the arrow keys on the Mac keyboard don't work properly for line editing in the jjs shell. But there is a hack for that: You can brew install rlwrap and use that as part of your alias for jjs in your .bashrc or .zshrc file.

Calling JavaScript from Java

To call Nashorn JavaScript from a Java 8 program, you basically need to make a new ScriptEngineManager instance and use that ScriptEngineManager to load the Nashorn script engine by name. (See this Stack Overflow question for a pithy summary of loading and debugging Nashorn.)

Finally, you can pass the Nashorn engine a file or a string to evaluate:

 import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; ... try { ScriptEngineManager factory = new ScriptEngineManager(); ScriptEngine engine = factory.getEngineByName("nashorn"); engine.eval("load(\"" + "src" + "/" + "javascript_sample" + "/" + "test1.js" + "\");"); } catch (Exception ex) { //... } ... try { ScriptEngineManager factory = new ScriptEngineManager(); ScriptEngine engine = factory.getEngineByName("nashorn"); engine.eval("function hi(){\nvar a = 'PROSPER'.toLowerCase(); \nmiddle(); \nprint('Live long and' + a)}\n function middle(){\n var b = 1; for(var i=0, max = 5; i
    

Note that scripts can always generate ScriptException errors, so you need to catch them.

Calling Java from JavaScript

Calling Java from Nashorn is about as easy as it can be, since the Java 8 class libraries are built into Nashorn:

 print(java.lang.System.currentTimeMillis()); var file = new java.io.File("sample.js"); print(file.getAbsolutePath()); print(file.absolutePath); 

Note that Nashorn does not import the java package by default, because references to String or Object conflict with the corresponding types in JavaScript. Hence, a Java string is java.lang.String, not String.

Nashorn and JavaFX

If you invoke jjs with the -fx switch, it will allow you to use visual JavaFX classes in your Nashorn applications. For instance, the following example from the Oracle documentation displays a JavaFX button:

 var Button = javafx.scene.control.Button; var StackPane = javafx.scene.layout.StackPane; var Scene = javafx.scene.Scene; function start(primaryStage) { primaryStage.title = "Hello World!"; var button = new Button(); button.text = "Say 'Hello World'"; button.onAction = function() print("Hello World!"); var root = new StackPane(); root.children.add(button); primaryStage.scene = new Scene(root, 300, 250); primaryStage.show(); } 

Debugging Nashorn

I mentioned earlier that Nashorn doesn't include a debugger of its own. Fortunately, both NetBeans 8 and IntelliJ IDEA 13.1 support debugging Nashorn JavaScript. The Stack Overflow question I mentioned earlier includes a useful NetBeans 8 project that you can use as a sample. You'll find that simply using the debug item from the pop-up menu on JavaScript files will allow you to debug the Nashorn code.

In IntelliJ IDEA 13, you can set breakpoints in the Java and Nashorn JavaScript files using the same shortcut key (Com/Ctrl-F8). When you hit a JavaScript breakpoint, you get all the usual debugging information.

Nashorn was designed to be a better, faster replacement for the old Rhino engine, and by most measures it succeeds. It has some minor warts that I hope will be corrected in future updates, but for now there are reasonable hacks to let you use Nashorn effectively in your projects.