Nashorn: JavaScript menjadi hebat di Java 8

Nashorn, dilafalkan "nass-horn," adalah bahasa Jerman untuk "badak", dan itu salah satu nama hewan untuk penghancur tank Jerman yang digunakan dalam Perang Dunia II. Itu juga merupakan nama pengganti - diperkenalkan dengan Java 8 - untuk mesin JavaScript Rhino yang lama dan lambat. Rhino dan Nashorn adalah implementasi dari bahasa JavaScript yang ditulis untuk dijalankan pada mesin virtual Java, atau JVM.

Kata-kata kasar wajib: JavaScript mungkin menggunakan Java sebagai bagian dari namanya, tetapi kedua bahasa tersebut sangat berbeda dalam semangat dan desain, serta dalam penerapannya. Namun demikian, salah satu cara untuk mengimplementasikan interpreter JavaScript adalah dengan mengkompilasi JavaScript ke dalam kode byte Java, yang dirancang untuk dilakukan oleh Rhino dan Nashorn.

Anda mungkin berpikir tentang JavaScript dalam istilah scripting browser Web, dan Anda akan benar untuk sebagian besar. Ini juga digunakan untuk server. Misalnya, Node.js digunakan untuk membangun server yang cepat dan ringan berdasarkan mesin JavaScript V8 dari Google Chrome. Mesin JavaScript di browser Web memiliki akses ke model objek dokumen (DOM) HTML dan dapat memanipulasi elemen HTML melalui DOM. Mengingat bahwa browser Web yang berbeda memiliki mesin DOM dan JavaScript yang berbeda, kerangka kerja seperti jQuery mencoba menyembunyikan detail implementasi dari pemrogram.

Nashorn, dan Rhino sebelumnya, secara eksplisit tidak mendukung DOM browser. Diimplementasikan pada JVM, mereka biasanya dipanggil untuk pembuatan skrip pengguna akhir dalam aplikasi Java. Nashorn dan Rhino dapat disematkan di program Java dan digunakan sebagai shell baris perintah. Tentu saja, keajaiban tambahan yang diperlukan saat Anda membuat skrip Java dari JavaScript adalah menjembatani ketidakcocokan data dan jenis antara dua bahasa.

Masalah dengan Badak

Pengembangan badak dimulai di Netscape pada tahun 1997 untuk proyek "Javagator" yang naas dan dirilis ke Mozilla.org pada tahun 1998. Kemudian dilisensikan kepada Sun dan lainnya. Sejujurnya, 1998 mungkin juga merupakan Periode Jurasik, seiring perkembangan internet - 16 tahun kemudian, Rhino dengan jelas menunjukkan usianya. Menurut Jim Laskey dari Oracle, pengembang utama Nashorn:

Saya yakin ini semua benar, tetapi sebagai pengembang dan manajer pengembangan yang letih, saya menemukan kalimat terakhir sangat lucu. Bagaimanapun, penulisan ulang besar tidak pernah menyenangkan. Memulai dari awal selalu menyenangkan.

Tujuan dari Nashorn

Laskey menjelaskan tujuannya untuk Nashorn sebagai berikut:

  • Nashorn akan didasarkan pada spesifikasi bahasa ECMAScript-262 Edition 5.1 dan harus lulus tes kepatuhan ECMAScript-262.
  • Nashorn akan mendukung javax.script(JSR 223) API.
  • Dukungan akan diberikan untuk menjalankan kode Java dari JavaScript dan untuk Java untuk memanggil kode JavaScript. Ini termasuk pemetaan langsung ke JavaBeans.
  • Nashorn akan mendefinisikan alat baris perintah baru jjs,, untuk mengevaluasi kode JavaScript dalam skrip "shebang", di sini mendokumentasikan, dan mengedit string.
  • Performa dan penggunaan memori aplikasi Nashorn harus jauh lebih baik daripada Rhino.
  • Nashorn tidak akan mengungkapkan risiko keamanan tambahan apa pun.
  • Perpustakaan yang disediakan harus berfungsi dengan benar di bawah pelokalan.
  • Pesan kesalahan dan dokumentasi akan diinternasionalkan.

Laskey juga secara eksplisit membatasi cakupan proyek dengan beberapa "non-tujuan":

  • Nashorn hanya akan mendukung ECMAScript-262 Edition 5.1. Ini tidak akan mendukung fitur Edisi 6 atau fitur tidak standar apa pun yang disediakan oleh implementasi JavaScript lainnya.
  • Nashorn tidak akan menyertakan API plugin browser.
  • Nashorn tidak akan menyertakan dukungan untuk DOM / CSS atau pustaka terkait (seperti jQuery, Prototype, atau Dojo).
  • Nashorn tidak akan menyertakan dukungan debugging langsung.

Jadi apa artinya berdasarkan ECMAScript-262 Edition 5.1? Perbedaannya di sini adalah bahwa Rhino didasarkan pada Edisi 3 yang lebih lama dan kurang mampu. javax.scriptAPI (JSR 223) digunakan untuk memanggil kembali JavaScript dari Java.

Kurangnya dukungan debugging di Nashorn adalah langkah mundur dari Rhino, yang memiliki debugger JavaScript sendiri. Namun, Anda akan menemukan solusi untuk penghilangan yang disengaja ini setidaknya dalam dua IDE populer.

Alat baris perintah Nashorn: Menginstal jjs dan jrunscript

Setelah membaca tentang alat baris perintah Nashorn jjs,, saya sangat ingin mencoba shell di iMac saya, tetapi setelah menginstal Java 8, itu tidak tersedia untuk shell bash. Ternyata dokumentasi dan implementasinya tidak sepenuhnya sinkron.

Saya tahu pemasangannya berhasil:

 >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) 

tapi lari jjskembali -bash: jjs: command not found. Sedikit mengaduk-aduk membawa saya ke /usr/bin/direktori:

 >which java /usr/bin/java 

Di sana saya menemukan sesuatu yang disebut jrunscript, yang ternyata merupakan varian dari jjsyang menjalankan skrip startup tambahan. Itu seharusnya memuaskan saya, tetapi saya bingung mengapa jjsalat yang didokumentasikan tidak diinstal /usr/bin/dengan sisa runtime Java 8 lainnya. Sedikit penelitian membawa saya untuk melihat JavaVirtualMachinesinstalasi untuk Java 8. Di Mac, cari jjsdi /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/bin/atau /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home/jre/bin/.

Anda dapat menentukan alias untuk jjsdi direktori terakhir dan menambahkannya ke konfigurasi shell Anda jika Anda membutuhkannya untuk pembuatan skrip di Mac atau Linux. Di PC, Anda dapat menambahkan jre/bin/direktori yang benar ke PATH. Dalam videonya dari peluncuran Java 8, Jim Laskey menyarankan jjsuntuk menyalin ke /usr/bin/direktori, tetapi ketika saya melakukannya, saya menemukan bahwa jjstidak dapat menemukan JRE dengan benar saat runtime.

Menjalankan skrip JavaScript

Mengapa dua alat baris perintah untuk menjalankan skrip JavaScript? Saya tidak sepenuhnya memahami apa yang dipikirkan oleh tim pengembangan, tetapi jjsmemiliki kemampuan yang jrunscripttidak, dan jrunscriptmemiliki file inisialisasi. Di bawah ini adalah beberapa contoh jjsdan jrunscriptpenggunaan sederhana.

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

Ini tidak berfungsi karena alert()merupakan fungsi browser / DOM. D'oh! Aku berani bersumpah itu berhasil di Rhino.

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

Ini berfungsi karena print () adalah fungsi inti JavaScript.

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

Dengan kata lain, kami memiliki lingkungan REPL (read-execute-print-loop command-line) dasar untuk JavaScript di sini. Jika Anda terkejut dengan jawabannya a+b, pertimbangkan ini:

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

Itu adalah efek samping yang menarik dari pengetikan yang longgar dan kelebihan beban operator "+" di JavaScript. Ini adalah perilaku yang benar menurut spesifikasi JavaScript, bukan bug.

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.