Pengenalan kepada Nashorn

1. Pengenalan

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

Banyak teknik canggih telah digunakan untuk membuat pesanan Nashorn yang lebih hebat daripada pendahulunya yang disebut Rhino, jadi itu adalah perubahan yang bermanfaat.

Mari kita lihat beberapa cara penggunaannya.

2. Perintah

JDK 1.8 merangkumi jurubahasa baris perintah yang disebut jj yang dapat digunakan untuk menjalankan fail JavaScript atau, jika dimulakan tanpa argumen, sebagai REPL (shell interaktif):

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

Di sini fail hello.js mengandungi satu arahan: cetak ("Hello World");

Kod yang sama dapat dijalankan secara interaktif:

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

Anda juga boleh memerintahkan * nix runtime untuk menggunakan jjs untuk menjalankan skrip sasaran dengan menambahkan #! $ JAVA_HOME / bin / jjs sebagai baris pertama:

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

Dan kemudian fail dapat dijalankan seperti biasa:

$ ./hello.js Hello World

3. Enjin Skrip Terbenam

Cara kedua dan mungkin lebih biasa untuk menjalankan JavaScript dari dalam JVM adalah melalui ScriptEngine. JSR-223 mendefinisikan sekumpulan API skrip, yang memungkinkan untuk seni bina mesin skrip yang dapat dipasang yang dapat digunakan untuk bahasa dinamis apa pun (dengan syarat, tentu saja, ia mempunyai pelaksanaan JVM)

Mari buat mesin JavaScript:

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

Di sini kami membuat ScriptEngineManager baru dan segera memintanya untuk memberi kami ScriptEngine bernama nashorn . Kemudian, kami memberikan beberapa arahan dan memperoleh hasil yang dapat diramalkan, ternyata menjadi " dunia hello " String .

4. Menyampaikan Data ke Skrip

Data dapat disalurkan ke dalam mesin dengan menentukan 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 coretan ini menghasilkan: " Hello baeldung baeldung baeldung ".

5. Memohon Fungsi JavaScript

Sudah tentu, boleh memanggil fungsi JavaScript dari kod Java anda:

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

Ini akan mengembalikan " Hello baeldung ".

6. Menggunakan Objek Java

Oleh kerana kita berjalan di JVM adalah mungkin untuk menggunakan objek Java asli dari dalam kode JavaScript.

Ini dicapai 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. Peluasan Bahasa

Nashorn mensasarkan ECMAScript 5.1 tetapi ia memberikan peluasan untuk menjadikan penggunaan JavaScript lebih baik.

7.1. Mengulangi Koleksi Dengan Untuk Setiap

Untuk-masing-masing adalah pelanjutan yang mudah untuk mempermudah pengulangan pelbagai 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 untuk setiap konstruk lelaran.

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

7.2. Literal Fungsi

Dalam deklarasi fungsi mudah, anda boleh menghilangkan pendakap keriting:

function increment(in) ++in

Jelas, ini hanya boleh dilakukan untuk fungsi satu-pelapik yang mudah.

7.3. Klausa Tangkap Bersyarat

Anda boleh menambahkan klausa tangkapan terlindung yang hanya dapat dilaksanakan jika keadaan yang ditentukan adalah 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 throw: BOOM ".

7.4. Susun atur Jenis dan Penukaran Jenis

Adalah mungkin untuk menggunakan array yang diketik Java dan untuk menukar 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 penukaran di sini untuk memastikan bahawa semua nilai dari tatasusunan JavaScript yang ditaip secara dinamik dapat masuk ke dalam array Java bilangan bulat.

Hasil panggilan di atas berfungsi dengan argumen [100, "1654", true] menghasilkan output 100, 1654 dan 1 (semua nombor).

Nilai String dan boolean secara implisit ditukar kepada rakan sebilangan bulat logiknya.

7.5. Menetapkan Prototaip 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 meneroka beberapa ciri enjin Nashorn J avaScript. Contoh yang ditunjukkan di sini menggunakan skrip literal rentetan, tetapi untuk senario kehidupan sebenar, kemungkinan besar anda ingin menyimpan skrip anda dalam fail yang berasingan dan memuatkannya menggunakan kelas Pembaca .

Seperti biasa, kod dalam penulisan ini tersedia di GitHub.