摘要:未來的主要發布基于。在中調用函數支持從代碼中直接調用定義在腳本文件中的函數。下面的函數稍后會在端調用為了調用函數,你首先需要將腳本引擎轉換為。調用函數將結果輸出到,所以我們會首先看到輸出。幸運的是,有一套補救措施。
原文:Java 8 Nashorn Tutorial
譯者:飛龍
協議:CC BY-NC-SA 4.0
這個教程中,你會通過簡單易懂的代碼示例,來了解Nashorn JavaScript引擎。Nashorn JavaScript引擎是Java SE 8 的一部分,并且和其它獨立的引擎例如Google V8(用于Google Chrome和Node.js的引擎)互相競爭。Nashorn通過在JVM上,以原生方式運行動態的JavaScript代碼來擴展Java的功能。
在接下來的15分鐘內,你會學到如何在JVM上在運行時動態執行JavaScript。我會使用小段代碼示例來演示最新的Nashron語言特性。你會學到如何在Java代碼中調用JavaScript函數,或者相反。最后你會準備好將動態腳本集成到你的Java日常業務中。
更新 - 我現在正在編寫用于瀏覽器的Java8數據流API的JavaScript實現。如果你對此感興趣,請在Github上訪問Stream.js。非常期待你的反饋。
使用 NashronNashorn JavaScript引擎可以在Java代碼中編程調用,也可以通過命令行工具jjs使用,它在$JAVA_HOME/bin中。如果打算使用jjs,你可能希望設置符號鏈接來簡化訪問:
$ cd /usr/bin $ ln -s $JAVA_HOME/bin/jjs jjs $ jjs jjs> print("Hello World");
這個教程專注于在Java代碼中調用Nashron,所以讓我們先跳過jjs。Java代碼中簡單的HelloWorld如下所示:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval("print("Hello World!");");
為了在Java中執行JavaScript,你首先要通過javax.script包創建腳本引擎。這個包已經在Rhino(來源于Mozilla、Java中的遺留JS引擎)中使用了。
JavaScript代碼既可以通過傳遞JavaScript代碼字符串,也可以傳遞指向你的JS腳本文件的FileReader來執行:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval(new FileReader("script.js"));
Nashorn JavaScript基于ECMAScript 5.1,但是它的后續版本會對ES6提供支持:
Nashorn的當前策略遵循ECMAScript規范。當我們在JDK8中發布它時,它將基于ECMAScript 5.1。Nashorn未來的主要發布基于ECMAScript 6。
Nashorn定義了大量對ECMAScript標準的語言和API擴展。但是首先讓我們看一看Java和JavaScript代碼如何交互。
在Java中調用JavaScript函數Nashorn 支持從Java代碼中直接調用定義在腳本文件中的JavaScript函數。你可以將Java對象傳遞為函數參數,并且從函數返回數據來調用Java方法。
下面的JavaScript函數稍后會在Java端調用:
var fun1 = function(name) { print("Hi there from Javascript, " + name); return "greetings from javascript"; }; var fun2 = function (object) { print("JS Class Definition: " + Object.prototype.toString.call(object)); };
為了調用函數,你首先需要將腳本引擎轉換為Invocable。Invocable接口由NashornScriptEngine實現,并且定義了invokeFunction方法來調用指定名稱的JavaScript函數。
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval(new FileReader("script.js")); Invocable invocable = (Invocable) engine; Object result = invocable.invokeFunction("fun1", "Peter Parker"); System.out.println(result); System.out.println(result.getClass()); // Hi there from Javascript, Peter Parker // greetings from javascript // class java.lang.String
執行這段代碼會在控制臺產生三行結果。調用函數print將結果輸出到System.out,所以我們會首先看到JavaScript輸出。
現在讓我們通過傳入任意Java對象來調用第二個函數:
invocable.invokeFunction("fun2", new Date()); // [object java.util.Date] invocable.invokeFunction("fun2", LocalDateTime.now()); // [object java.time.LocalDateTime] invocable.invokeFunction("fun2", new Person()); // [object com.winterbe.java8.Person]
Java對象在傳入時不會在JavaScript端損失任何類型信息。由于腳本在JVM上原生運行,我們可以在Nashron上使用Java API或外部庫的全部功能。
在JavaScript中調用Java方法在JavaScript中調用Java方法十分容易。我們首先需要定義一個Java靜態方法。
static String fun1(String name) { System.out.format("Hi there from Java, %s", name); return "greetings from java"; }
Java類可以通過Java.typeAPI擴展在JavaScript中引用。它就和Java代碼中的import類似。只要定義了Java類型,我們就可以自然地調用靜態方法fun1(),然后像sout打印信息。由于方法是靜態的,我們不需要首先創建實例。
var MyJavaClass = Java.type("my.package.MyJavaClass"); var result = MyJavaClass.fun1("John Doe"); print(result); // Hi there from Java, John Doe // greetings from java
在使用JavaScript原生類型調用Java方法時,Nashorn 如何處理類型轉換?讓我們通過簡單的例子來弄清楚。
下面的Java方法簡單打印了方法參數的實際類型:
static void fun2(Object object) { System.out.println(object.getClass()); }
為了理解背后如何處理類型轉換,我們使用不同的JavaScript類型來調用這個方法:
MyJavaClass.fun2(123); // class java.lang.Integer MyJavaClass.fun2(49.99); // class java.lang.Double MyJavaClass.fun2(true); // class java.lang.Boolean MyJavaClass.fun2("hi there") // class java.lang.String MyJavaClass.fun2(new Number(23)); // class jdk.nashorn.internal.objects.NativeNumber MyJavaClass.fun2(new Date()); // class jdk.nashorn.internal.objects.NativeDate MyJavaClass.fun2(new RegExp()); // class jdk.nashorn.internal.objects.NativeRegExp MyJavaClass.fun2({foo: "bar"}); // class jdk.nashorn.internal.scripts.JO4
JavaScript原始類型轉換為合適的Java包裝類,而JavaScript原生對象會使用內部的適配器類來表示。要記住jdk.nashorn.internal中的類可能會有所變化,所以不應該在客戶端面向這些類來編程。
ScriptObjectMirror任何標記為“內部”的東西都可能會從你那里發生改變。
在向Java傳遞原生JavaScript對象時,你可以使用ScriptObjectMirror類,它實際上是底層JavaScript對象的Java表示。ScriptObjectMirror實現了Map接口,位于jdk.nashorn.api中。這個包中的類可以用于客戶端代碼。
下面的例子將參數類型從Object改為ScriptObjectMirror,所以我們可以從傳入的JavaScript對象中獲得一些信息。
static void fun3(ScriptObjectMirror mirror) { System.out.println(mirror.getClassName() + ": " + Arrays.toString(mirror.getOwnKeys(true))); }
當向這個方法傳遞對象(哈希表)時,在Java端可以訪問其屬性:
MyJavaClass.fun3({ foo: "bar", bar: "foo" }); // Object: [foo, bar]
我們也可以在Java中調用JavaScript的成員函數。讓我們首先定義JavaScript Person類型,帶有屬性firstName 和 lastName,以及方法getFullName。
function Person(firstName, lastName) { this.firstName = firstName; this.lastName = lastName; this.getFullName = function() { return this.firstName + " " + this.lastName; } }
JavaScript方法getFullName可以通過callMember()在ScriptObjectMirror 上調用。
static void fun4(ScriptObjectMirror person) { System.out.println("Full Name is: " + person.callMember("getFullName")); }
當向Java方法傳遞新的Person時,我們會在控制臺看到預期的結果:
var person1 = new Person("Peter", "Parker"); MyJavaClass.fun4(person1); // Full Name is: Peter Parker語言擴展
Nashorn定義了多種對ECMAScript標準的語言和API擴展。讓我們看一看最新的特性:
類型數組JavaScript的原生數組是無類型的。Nashron允許你在JavaScript中使用Java的類型數組:
var IntArray = Java.type("int[]"); var array = new IntArray(5); array[0] = 5; array[1] = 4; array[2] = 3; array[3] = 2; array[4] = 1; try { array[5] = 23; } catch (e) { print(e.message); // Array index out of range: 5 } array[0] = "17"; print(array[0]); // 17 array[0] = "wrong type"; print(array[0]); // 0 array[0] = "17.3"; print(array[0]); // 17
int[]數組就像真實的Java整數數組那樣。但是此外,在我們試圖向數組添加非整數時,Nashron在背后執行了一些隱式的轉換。字符串會自動轉換為整數,這十分便利。
集合和范圍遍歷我們可以使用任何Java集合,而避免使用數組瞎折騰。首先需要通過Java.type定義Java類型,之后創建新的實例。
var ArrayList = Java.type("java.util.ArrayList"); var list = new ArrayList(); list.add("a"); list.add("b"); list.add("c"); for each (var el in list) print(el); // a, b, c
為了迭代集合和數組,Nashron引入了for each語句。它就像Java的范圍遍歷那樣工作。
下面是另一個集合的范圍遍歷示例,使用HashMap:
var map = new java.util.HashMap(); map.put("foo", "val1"); map.put("bar", "val2"); for each (var e in map.keySet()) print(e); // foo, bar for each (var e in map.values()) print(e); // val1, val2Lambda表達式和數據流
每個人都熱愛lambda和數據流 -- Nashron也一樣!雖然ECMAScript 5.1沒有Java8 lmbda表達式的簡化箭頭語法,我們可以在任何接受lambda表達式的地方使用函數字面值。
var list2 = new java.util.ArrayList(); list2.add("ffffd2"); list2.add("aaa2"); list2.add("bbb1"); list2.add("aaa1"); list2.add("bbb3"); list2.add("ccc"); list2.add("bbb2"); list2.add("ffffd1"); list2 .stream() .filter(function(el) { return el.startsWith("aaa"); }) .sorted() .forEach(function(el) { print(el); }); // aaa1, aaa2類的繼承
Java類型可以由Java.extend輕易擴展。就像你在下面的例子中看到的那樣,你甚至可以在你的腳本中創建多線程的代碼:
var Runnable = Java.type("java.lang.Runnable"); var Printer = Java.extend(Runnable, { run: function() { print("printed from a separate thread"); } }); var Thread = Java.type("java.lang.Thread"); new Thread(new Printer()).start(); new Thread(function() { print("printed from another thread"); }).start(); // printed from a separate thread // printed from another thread參數重載
方法和函數可以通過點運算符或方括號運算符來調用:
var System = Java.type("java.lang.System"); System.out.println(10); // 10 System.out["println"](11.0); // 11.0 System.out["println(double)"](12); // 12.0
當使用重載參數調用方法時,傳遞可選參數類型println(double)會指定所調用的具體方法。
Java Beans你可以簡單地使用屬性名稱來向Java Beans獲取或設置值,不需要顯式調用讀寫器:
var Date = Java.type("java.util.Date"); var date = new Date(); date.year += 1900; print(date.year); // 2014函數字面值
對于簡單的單行函數,我們可以去掉花括號:
function sqr(x) x * x; print(sqr(3)); // 9屬性綁定
兩個不同對象的屬性可以綁定到一起:
var o1 = {}; var o2 = { foo: "bar"}; Object.bindProperties(o1, o2); print(o1.foo); // bar o1.foo = "BAM"; print(o2.foo); // BAM字符串去空白
我喜歡去掉空白的字符串:
print(" hehe".trimLeft()); // hehe print("hehe ".trimRight() + "he"); // hehehe位置
以防你忘了自己在哪里:
print(__FILE__, __LINE__, __DIR__);導入作用域
有時一次導入多個Java包會很方便。我們可以使用JavaImporter類,和with語句一起使用。所有被導入包的類文件都可以在with語句的局部域中訪問到。
var imports = new JavaImporter(java.io, java.lang); with (imports) { var file = new File(__FILE__); System.out.println(file.getAbsolutePath()); // /path/to/my/script.js }數組轉換
一些類似java.util的包可以不使用java.type或JavaImporter直接訪問:
var list = new java.util.ArrayList(); list.add("s1"); list.add("s2"); list.add("s3");
下面的代碼將Java列表轉換為JavaScript原生數組:
var jsArray = Java.from(list); print(jsArray); // s1,s2,s3 print(Object.prototype.toString.call(jsArray)); // [object Array]
下面的代碼執行相反操作:
var javaArray = Java.to([3, 5, 7, 11], "int[]");訪問超類
在JavaScript中訪問被覆蓋的成員通常比較困難,因為Java的super關鍵字在ECMAScript中并不存在。幸運的是,Nashron有一套補救措施。
首先我們需要在Java代碼中定義超類:
class SuperRunner implements Runnable { @Override public void run() { System.out.println("super run"); } }
下面我在JavaScript中覆蓋了SuperRunner。要注意創建新的Runner實例時的Nashron語法:覆蓋成員的語法取自Java的匿名對象。
var SuperRunner = Java.type("com.winterbe.java8.SuperRunner"); var Runner = Java.extend(SuperRunner); var runner = new Runner() { run: function() { Java.super(runner).run(); print("on my run"); } } runner.run(); // super run // on my run
我們通過Java.super()擴展調用了被覆蓋的SuperRunner.run()方法。
加載腳本在JavaScript中加載額外的腳本文件非常方便。我們可以使用load函數加載本地或遠程腳本。
我在我的Web前端中大量使用Underscore.js,所以讓我們在Nashron中復用它:
load("http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"); var odds = _.filter([1, 2, 3, 4, 5, 6], function (num) { return num % 2 == 1; }); print(odds); // 1, 3, 5
外部腳本會在相同JavaScript上下文中被執行,所以我們可以直接訪問underscore 的對象。要記住當變量名稱互相沖突時,腳本的加載可能會使你的代碼崩潰。
這一問題可以通過把腳本文件加載到新的全局上下文來繞過:
loadWithNewGlobal("script.js");命令行腳本
如果你對編寫命令行(shell)腳本感興趣,來試一試Nake吧。Nake是一個Java 8 Nashron的簡化構建工具。你只需要在項目特定的Nakefile中定義任務,之后通過在命令行鍵入nake -- myTask來執行這些任務。任務編寫為JavaScript,并且在Nashron的腳本模式下運行,所以你可以使用你的終端、JDK8 API和任意Java庫的全部功能。
對Java開發者來說,編寫命令行腳本是前所未有的簡單...
到此為止我希望這個教程對你有所幫助,并且你能夠享受Nashron JavaScript引擎之旅。有關Nashron的更多信息,請見這里、這里和這里。使用Nashron編寫shell腳本的教程請見這里。
我最近發布了一篇后續文章,關于如何在Nashron中使用Backbone.js模型。如果你想要進一步學習Java8,請閱讀我的Java8教程,和我的Java8數據流教程。
這篇Nashron教程中的可運行的源代碼托管在Github上。請隨意fork我的倉庫,或者在Twitter上向我反饋。
請堅持編程!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64961.html
摘要:原文譯者飛龍協議這個例子展示了如何在的引擎中使用模型。在年三月首次作為的一部分發布,并通過以原生方式在上運行腳本擴展了的功能。將二者放在一起下一個目標是在中,例如在服務器上復用模型。最后,我們在中調用函數。總結在中復用現存的庫十分簡單。 原文:Using Backbone.js with Nashorn 譯者:飛龍 協議:CC BY-NC-SA 4.0 這個例子展示了如何在Java8...
摘要:原文譯者飛龍協議這個例子展示了如何在的引擎中使用模型。在年三月首次作為的一部分發布,并通過以原生方式在上運行腳本擴展了的功能。將二者放在一起下一個目標是在中,例如在服務器上復用模型。最后,我們在中調用函數。總結在中復用現存的庫十分簡單。 原文:Using Backbone.js with Nashorn 譯者:飛龍 協議:CC BY-NC-SA 4.0 這個例子展示了如何在Java8...
摘要:示例字符串數值算術和文件原文譯者飛龍協議大量的教程和文章都涉及到中最重要的改變,例如表達式和函數式數據流。不僅僅是字符串,正則表達式模式串也能受益于數據流。 Java 8 API 示例:字符串、數值、算術和文件 原文:Java 8 API by Example: Strings, Numbers, Math and Files 譯者:飛龍 協議:CC BY-NC-SA 4.0 ...
簡介 毫無疑問,Java 8是Java自Java 5(發布于2004年)之后的最重要的版本。這個版本包含語言、編譯器、庫、工具和JVM等方面的十多個新特性。在本文中我們將學習這些新特性,并用實際的例子說明在什么場景下適合使用。 這個教程包含Java開發者經常面對的幾類問題: 語言編譯器庫工具運行時(JVM)新的Java工具 Java 8提供了一些新的命令行工具,這部分會講解一些對開發者最有用的工具...
摘要:未來的主要發布基于。在中調用函數支持從代碼中直接調用定義在腳本文件中的函數。內置了方法,提調用函數并返回結果。當向方法傳遞新的時,我們會在控制臺看到預期的結果實戰通過使用實現服務端渲染。工具類實例化工具類,通過該類操作對象。 Nashorn JavaScript引擎是Java SE 8 的一部分,并且和其它獨立的引擎例如 Google V8(用于Google Chrome和Node.j...
閱讀 3351·2021-10-13 09:40
閱讀 2586·2021-10-08 10:17
閱讀 3989·2021-09-28 09:45
閱讀 922·2021-09-28 09:35
閱讀 1805·2019-08-30 10:51
閱讀 2898·2019-08-26 12:11
閱讀 1645·2019-08-26 10:41
閱讀 3091·2019-08-23 17:10