Pengantar Nashorn

1. Perkenalan

Artikel ini difokuskan pada Nashorn - mesin JavaScript default baru untuk JVM pada Java 8.

Banyak teknik canggih telah digunakan untuk membuat pesanan Nashorn lebih berkinerja daripada pendahulunya yang disebut Rhino, jadi ini adalah perubahan yang berharga.

Mari kita lihat beberapa cara penggunaannya.

2. Baris Perintah

JDK 1.8 menyertakan penerjemah baris perintah yang disebut jjs yang dapat digunakan untuk menjalankan file JavaScript atau, jika dimulai tanpa argumen, sebagai REPL (shell interaktif):

$ $JAVA_HOME/bin/jjs hello.js Hello World

Di sini file hello.js berisi satu instruksi: print ("Hello World");

Kode yang sama dapat dijalankan secara interaktif:

$ $JAVA_HOME/bin/jjs jjs> print("Hello World") Hello World

Anda juga dapat menginstruksikan * nix runtime untuk menggunakan jjs untuk menjalankan skrip target dengan menambahkan #! $ JAVA_HOME / bin / jjs sebagai baris pertama:

#!$JAVA_HOME/bin/jjs var greeting = "Hello World"; print(greeting);

Dan kemudian file tersebut dapat dijalankan seperti biasa:

$ ./hello.js Hello World

3. Mesin Skrip Tertanam

Cara kedua dan mungkin lebih umum untuk menjalankan JavaScript dari dalam JVM adalah melalui ScriptEngine. JSR-223 mendefinisikan satu set API skrip, memungkinkan arsitektur mesin skrip yang dapat dicolokkan yang dapat digunakan untuk bahasa dinamis apa pun (asalkan memiliki implementasi JVM, tentu saja).

Mari buat mesin JavaScript:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); Object result = engine.eval( "var greeting="hello world";" + "print(greeting);" + "greeting");

Di sini kita membuat baru ScriptEngineManager dan segera meminta untuk memberi kita ScriptEngine bernama Nashorn . Kemudian, kami memberikan beberapa instruksi dan mendapatkan hasil yang dapat diprediksi, ternyata adalah Stringhello world ”.

4. Meneruskan Data ke Script

Data dapat diteruskan ke mesin dengan mendefinisikan objek Bindings dan meneruskannya sebagai parameter kedua ke fungsi eval :

Bindings bindings = engine.createBindings(); bindings.put("count", 3); bindings.put("name", "baeldung"); String script = "var greeting="Hello";" + "for(var i=count;i>0;i--) { " + "greeting+=name + ' '" + "}" + "greeting"; Object bindingsResult = engine.eval(script, bindings);

Menjalankan cuplikan ini menghasilkan: “ Hello baeldung baeldung baeldung “.

5. Memanggil Fungsi JavaScript

Ini, tentu saja, memungkinkan untuk memanggil fungsi JavaScript dari kode Java Anda:

engine.eval("function composeGreeting(name) {" + "return 'Hello ' + name" + "}"); Invocable invocable = (Invocable) engine; Object funcResult = invocable.invokeFunction("composeGreeting", "baeldung");

Ini akan mengembalikan " Halo baeldung ".

6. Menggunakan Objek Java

Karena kami berjalan di JVM, dimungkinkan untuk menggunakan objek Java asli dari dalam kode JavaScript.

Ini dilakukan dengan menggunakan objek Java :

Object map = engine.eval("var HashMap = Java.type('java.util.HashMap');" + "var map = new HashMap();" + "map.put('hello', 'world');" + "map");

7. Ekstensi Bahasa

Nashorn menargetkan ECMAScript 5.1 tetapi ia menyediakan ekstensi untuk membuat penggunaan JavaScript sedikit lebih baik.

7.1. Mengulangi Koleksi Dengan Untuk-Masing-Masing

Untuk masing-masing adalah ekstensi yang nyaman untuk mempermudah pengulangan berbagai koleksi:

String script = "var list = [1, 2, 3, 4, 5];" + "var result = '';" + "for each (var i in list) {" + "result+=i+'-';" + "};" + "print(result);"; engine.eval(script);

Di sini, kami menggabungkan elemen array dengan menggunakan for-each iteration construct.

Output yang dihasilkan akan menjadi 1-2-3-4-5- .

7.2. Fungsi Literal

Dalam deklarasi fungsi sederhana, Anda dapat menghilangkan tanda kurung kurawal:

function increment(in) ++in

Jelas, ini hanya dapat dilakukan untuk fungsi satu baris yang sederhana.

7.3. Klausul Tangkapan Bersyarat

Anda dapat menambahkan klausa tangkapan yang dilindungi yang hanya dijalankan jika kondisi yang ditentukan benar:

try { throw "BOOM"; } catch(e if typeof e === 'string') { print("String thrown: " + e); } catch(e) { print("this shouldn't happen!"); }

Ini akan mencetak " String dilempar: BOOM ".

7.4. Konversi Jenis dan Jenis Konversi

Dimungkinkan untuk menggunakan array yang diketik Java dan untuk mengonversi ke dan dari array JavaScript:

function arrays(arr) { var javaIntArray = Java.to(arr, "int[]"); print(javaIntArray[0]); print(javaIntArray[1]); print(javaIntArray[2]); }

Nashorn melakukan beberapa jenis konversi di sini untuk memastikan bahwa semua nilai dari larik JavaScript yang diketik secara dinamis dapat masuk ke dalam larik Java hanya bilangan bulat.

Hasil pemanggilan fungsi di atas dengan argumen [100, "1654", true] menghasilkan output 100, 1654 dan 1 (semua angka).

Nilai String dan boolean secara implisit dikonversi ke padanan integer logisnya.

7.5. Mengatur Prototipe Objek Dengan Object.setPrototypeOf

Nashorn defines an API extension that enables us to change the prototype of an object:

Object.setPrototypeOf(obj, newProto)

This function is generally considered a better alternative to Object.prototype.__proto__ so it should be the preferred way to set object's prototype in all new code.

7.6. Magical __noSuchProperty__ and __noSuchMethod__

It is possible to define methods on an object that will be invoked whenever an undefined property is accessed or an undefined method is invoked:

var demo = { __noSuchProperty__: function (propName) { print("Accessed non-existing property: " + propName); }, __noSuchMethod__: function (methodName) { print("Invoked non-existing method: " + methodName); } }; demo.doesNotExist; demo.callNonExistingMethod()

This will print:

Accessed non-existing property: doesNotExist Invoked non-existing method: callNonExistingMethod

7.7. Bind Object Properties With Object.bindProperties

Object.bindProperties can be used to bind properties from one object into another:

var first = { name: "Whiskey", age: 5 }; var second = { volume: 100 }; Object.bindProperties(first, second); print(first.volume); second.volume = 1000; print(first.volume);

Notice, that this creates is a “live” binding and any updates to the source object are also visible through the binding target.

7.8. Locations

Current file name, directory and a line can be obtained from global variables __FILE__, __DIR__, __LINE__:

print(__FILE__, __LINE__, __DIR__)

7.9. Extensions to String.prototype

There are two simple, but very useful extensions that Nashorn provides on the String prototype. These are trimRight and trimLeft functions which, unsurprisingly, return a copy of the String with the whitespace removed:

print(" hello world".trimLeft()); print("hello world ".trimRight());

Will print “hello world” twice without leading or trailing spaces.

7.10. Java.asJSONCompatible Function

Using this function, we can obtain an object that is compatible with Java JSON libraries expectations.

Namely, that if it itself, or any object transitively reachable through it is a JavaScript array, then such objects will be exposed as JSObject that also implements the List interface for exposing the array elements.

Object obj = engine.eval("Java.asJSONCompatible( { number: 42, greet: 'hello', primes: [2,3,5,7,11,13] })"); Map map = (Map)obj; System.out.println(map.get("greet")); System.out.println(map.get("primes")); System.out.println(List.class.isAssignableFrom(map.get("primes").getClass()));

This will print “hello” followed by [2, 3, 5, 7, 11, 13] followed by true.

8. Loading Scripts

It's also possible to load another JavaScript file from within the ScriptEngine:

load('classpath:script.js')

A script can also be loaded from a URL:

load('/script.js')

Keep in mind that JavaScript does not have a concept of namespaces so everything gets piled on into the global scope. This makes it possible for loaded scripts to create naming conflicts with your code or each other. This can be mitigated by using the loadWithNewGlobal function:

var math = loadWithNewGlobal('classpath:math_module.js') math.increment(5);

With the following math_module.js:

var math = { increment: function(num) { return ++num; } }; math;bai

Here we are defining an object named math that has a single function called increment. Using this paradigm we can even emulate basic modularity!

8. Conclusion

Artikel ini membahas beberapa fitur mesin avaScript J Nashorn . Contoh yang dipamerkan di sini menggunakan skrip literal string, tetapi untuk skenario kehidupan nyata, Anda kemungkinan besar ingin menyimpan skrip Anda dalam file terpisah dan memuatnya menggunakan kelas Pembaca .

Seperti biasa, kode dalam artikel ini tersedia di GitHub.