自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java 8的Nashorn腳本引擎教程

開發(fā) 后端
本文為了解所有關(guān)于 Nashorn JavaScript 引擎易于理解的代碼例子。 Nashorn JavaScript 引擎是Java SE 8的一部分,它與其它像Google V8 (它是Google Chrome 和Node.js的引擎)的獨(dú)立引擎相互競(jìng)爭(zhēng)。 Nashorn 擴(kuò)展了Java在JVM上運(yùn)行動(dòng)態(tài)JavaScript腳本的能力。

本文為了解所有關(guān)于 Nashorn JavaScript 引擎易于理解的代碼例子。 Nashorn JavaScript 引擎是Java SE 8的一部分,它與其它像Google V8 (它是Google Chrome 和Node.js的引擎)的獨(dú)立引擎相互競(jìng)爭(zhēng)。 Nashorn 擴(kuò)展了Java在JVM上運(yùn)行動(dòng)態(tài)JavaScript腳本的能力。

[[178990]]

在接下來的大約15分鐘里,您將學(xué)習(xí)如何在 JVM 上動(dòng)態(tài)運(yùn)行 JavaScript。 通過一些簡(jiǎn)短的代碼示例演示最近 Nashorn 的語言特性。 學(xué)習(xí) Java 與 JavaScript 的相互調(diào)用。***包括如何在日常的 Java 業(yè)務(wù)中整合動(dòng)態(tài)腳本。

使用Nashorn

Nashorn javascript 引擎要么在java程序中以編程的方式使用要么在命令行工具jjs使用,jjs在目錄$JAVA_HOME/bin中。如果你準(zhǔn)備建立一個(gè)jjs的符號(hào)鏈接,如下:

 

  1. $ cd /usr/bin 
  2. $ ln -s $JAVA_HOME/bin/jjs jjs 
  3. $ jjs 
  4. jjs> print('Hello World'); 

本教程關(guān)注的是在java代碼中使用 nashorn ,所以我們現(xiàn)在跳過jjs。用java代碼來一個(gè)簡(jiǎn)單的  HelloWorld示例,如下:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval("print('Hello World!');");

為了在java中執(zhí)行JavaScript代碼,首先使用原先Rhino (舊版Java中來自Mozilla的引擎)中的包javax.script來創(chuàng)建一個(gè)nashorn腳本引擎。.

既可以向上面那樣把JavaScript代碼作為一個(gè)字符串來直接執(zhí)行,也可放入一個(gè)js腳本文件中,如:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); engine.eval(new FileReader("script.js"));

Nashorn javascript是基于 ECMAScript 5.1 ,但nashorn后續(xù)版本將支持 ECMAScript 6:

當(dāng)前Nashorn的策略是遵循ECMAScript規(guī)范。 當(dāng)我們發(fā)布JDK 8時(shí),我們將實(shí)現(xiàn)ECMAScript 5.1標(biāo)準(zhǔn)。后續(xù)的 Nashorn的版本將實(shí)現(xiàn) ECMAScript Edition 6標(biāo)準(zhǔn)。

Nashorn定義了很多語言和擴(kuò)展了 ECMAScript標(biāo)準(zhǔn)的API 。接下來我們看看java與JavaScript的通信。

Java調(diào)用Javascript 函數(shù)

Nashorn 支持java代碼直接調(diào)用定義在腳本文件中JavaScript函數(shù)。你可以把java對(duì)象作為函數(shù)的參數(shù)且在調(diào)用函數(shù)的java方法中接收返回的數(shù)據(jù)。

如下的JavaScript代碼將會(huì)在java端調(diào)用:

  1. var fun1 = function(name) { 
  2.     print('Hi there from Javascript, ' + name); 
  3.     return "greetings from javascript"; }; var fun2 = function (object) { print("JS Class Definition: " + Object.prototype.toString.call(object)); 
  4. }; 

為了調(diào)用函數(shù),你首先得把腳本引擎轉(zhuǎn)換為 Invocable。NashornScriptEngine 實(shí)現(xiàn)了 Invocable 接口且定義一個(gè)調(diào)用JavaScript函數(shù)的方法 invokeFunction ,傳入函數(shù)名即可。

 

  1. ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); 
  2. engine.eval(new FileReader("script.js")); 
  3.  
  4. Invocable invocable = (Invocable) engine; 
  5.  
  6. Object result = invocable.invokeFunction("fun1""Peter Parker"); 
  7. System.out.println(result); 
  8. System.out.println(result.getClass()); 
  9.  
  10. // Hi there from Javascript, Peter Parker 
  11. // greetings from javascript 
  12. // class java.lang.String 

上述代碼的執(zhí)行將在控制臺(tái)打印三行信息。調(diào)用 print 函數(shù)將輸出內(nèi)容通過管道送到 System.out 控制臺(tái),因此我們首先看到的是 JavaScript打印的信息。

現(xiàn)在我們通過傳遞任意的 Java 對(duì)象去調(diào)用第二個(gè)函數(shù):

 

  1. invocable.invokeFunction("fun2"new Date()); 
  2. // [object java.util.Date] 
  3.  
  4. invocable.invokeFunction("fun2", LocalDateTime.now()); 
  5. // [object java.time.LocalDateTime] 
  6.  
  7. invocable.invokeFunction("fun2"new Person()); 
  8. // [object com.winterbe.java8.Person] 

你可以傳遞任意 Java 對(duì)象而不會(huì)在 JavaScript 這邊丟失類型信息。因?yàn)槟_本本身是在 JVM 虛擬機(jī)中執(zhí)行的,我們可以完全利用 nashorn 引擎的 Java API 和外部庫的強(qiáng)大功能。

在 JavaScript 端調(diào)用 Java 方法

在 JavaScript 中調(diào)用 Java 方法很簡(jiǎn)單。首先我們定義一個(gè)靜態(tài)的 Java 方法:

 

  1. static String fun1(String name) { 
  2.     System.out.format("Hi there from Java, %s", name); 
  3.     return "greetings from java"

JavaScript 可通過 Java.type API 來引用 Java 類。這跟在 Java 類中引入其他類是類似的。當(dāng)定義了 Java 類型后我們可直接調(diào)用其靜態(tài)方法 fun1() 并打印結(jié)果到 sout。因?yàn)榉椒ㄊ庆o態(tài)的,所以我們無需創(chuàng)建類實(shí)例。

 

  1. var MyJavaClass = Java.type('my.package.MyJavaClass'); 
  2. var result = MyJavaClass.fun1('John Doe'); 
  3. print(result); 
  4. // Hi there from Java, John Doe 
  5. // greetings from java 

 

當(dāng)調(diào)用java 方法時(shí),Nashorn怎樣處理原生JavaScript類型與java類型轉(zhuǎn)換?讓我們用一個(gè)簡(jiǎn)單的例子來發(fā)現(xiàn)。

下面的java方法簡(jiǎn)單打印實(shí)際的類方法參數(shù)的類型:

 

  1. static void fun2(Object object) { 
  2.     System.out.println(object.getClass()); 

為了解引擎如何處理類型轉(zhuǎn)換,我使用不同JavaScript類型來調(diào)用java方法:

 

  1. MyJavaClass.fun2(123); 
  2. // class java.lang.Integer 
  3.  
  4. MyJavaClass.fun2(49.99); 
  5. // class java.lang.Double 
  6.  
  7. MyJavaClass.fun2(true); 
  8. // class java.lang.Boolean 
  9.  
  10. MyJavaClass.fun2("hi there"
  11. // class java.lang.String 
  12.  
  13. MyJavaClass.fun2(new Number(23)); 
  14. // class jdk.nashorn.internal.objects.NativeNumber 
  15.  
  16. MyJavaClass.fun2(new Date()); 
  17. // class jdk.nashorn.internal.objects.NativeDate 
  18.  
  19. MyJavaClass.fun2(new RegExp()); 
  20. // class jdk.nashorn.internal.objects.NativeRegExp 
  21.  
  22. MyJavaClass.fun2({foo: 'bar'}); 
  23. // class jdk.nashorn.internal.scripts.JO4 

 

原始的javascript 類型被轉(zhuǎn)換為適當(dāng)?shù)?nbsp;java 包裝器類。而不是本地javascript對(duì)象內(nèi)部適配器類。請(qǐng)記住,這些類來自于jdk.nashorn.internal,所以你不應(yīng)該在客戶端使用這些類:

Anything marked internal will likely change out from underneath you.

ScriptObjectMirror

當(dāng)使用ScriptObjectMirror把本地JavaScript對(duì)象傳入時(shí),實(shí)際上是有一個(gè)java對(duì)象表示JavaScript 對(duì)象。 ScriptObjectMirror 實(shí)現(xiàn)了接口與jdk.nashorn.api內(nèi)部的映射。這個(gè)包下的類目的就是用于客戶端代碼使用。

下一個(gè)示例更改參數(shù)類型Object為ScriptObjectMirror,因此我們能獲取到傳入JavaScript中對(duì)象的一些信息:

 

  1. static void fun3(ScriptObjectMirror mirror) { 
  2.     System.out.println(mirror.getClassName() + ": " + 
  3.         Arrays.toString(mirror.getOwnKeys(true))); 

當(dāng)我們把傳遞對(duì)象hash到方法中,在Java端就能訪問這些屬性:

 

  1. MyJavaClass.fun3({ 
  2.     foo: 'bar'
  3.     bar: 'foo' 
  4. }); 
  5.  
  6. // Object: [foo, bar] 

 

我們也可以在Java端調(diào)用JavaScript對(duì)象中的函數(shù)。我們首先定義一個(gè)JavaScript類型 Person,包含屬性 firstName 、lastName 和函數(shù)getFullName。

 

  1. function Person(firstName, lastName) { 
  2.     this.firstName = firstName; 
  3.     this.lastName = lastName; 
  4.     this.getFullName = function() { 
  5.         return this.firstName + " " + this.lastName; 
  6.     } 

javascript 函數(shù)getFullName 能被 ScriptObjectMirror 的callMember()調(diào)用。

 

  1. static void fun4(ScriptObjectMirror person) { 
  2.     System.out.println("Full Name is: " + person.callMember("getFullName")); 

當(dāng)我們傳入一個(gè)新的person給java 方法時(shí),我們能在控制臺(tái)看到預(yù)期結(jié)果:

 

  1. var person1 = new Person("Peter""Parker"); 
  2. MyJavaClass.fun4(person1); 
  3.  
  4. // Full Name is: Peter Parker 

語言擴(kuò)展

Nashorn 定義一系列的語言和擴(kuò)展了 ECMAScript 標(biāo)準(zhǔn)的API。 讓我們直接進(jìn)入***的功能:

類型數(shù)組

原始javascript 數(shù)組時(shí)無類型的。 Nashorn 運(yùn)行你在JavaScript中使用java數(shù)組:

 

  1. var IntArray = Java.type("int[]"); 
  2.  
  3. var array = new IntArray(5); 
  4. array[0] = 5
  5. array[1] = 4
  6. array[2] = 3
  7. array[3] = 2
  8. array[4] = 1
  9.  
  10. try { 
  11.     array[5] = 23
  12. catch (e) { 
  13.     print(e.message);  // Array index out of range: 5 
  14.  
  15. array[0] = "17"
  16. print(array[0]);  // 17 
  17.  
  18. array[0] = "wrong type"
  19. print(array[0]);  // 0 
  20.  
  21. array[0] = "17.3"
  22. print(array[0]);  // 17 

int[] 數(shù)組的行為像一個(gè)真正的 java int 數(shù)組。 但當(dāng)我們?cè)噲D添加非整數(shù)的值的數(shù)組時(shí),Nashorn 會(huì)執(zhí)行隱式類型轉(zhuǎn)換。 字符串會(huì)自動(dòng)轉(zhuǎn)換為int,這相當(dāng)方便。

集合與For Each

我們可以使用java的集合來代替數(shù)組。首先定義使用 Java.type定義一個(gè)java類型,而后根據(jù)需要?jiǎng)?chuàng)建一個(gè)實(shí)例。

 

  1. var ArrayList = Java.type('java.util.ArrayList'); 
  2. var list = new ArrayList(); 
  3. list.add('a'); 
  4. list.add('b'); 
  5. list.add('c'); 
  6.  
  7. for each (var el in list) print(el);  // a, b, c

為了遍歷集合和數(shù)組中的元素,Nashorn 引入了 for each 語句。這就像是 Java 的 for 循環(huán)一樣。

這里是一個(gè)對(duì)集合元素進(jìn)行遍歷的例子,使用的是 :

 

  1. var map = new java.util.HashMap(); 
  2. map.put('foo''val1'); 
  3. map.put('bar''val2'); 
  4.  
  5. for each (var e in map.keySet()) print(e);  // foo, bar 
  6. for each (var e in map.values()) print(e);  // val1, val2 

 

Lambda 表達(dá)式和 Streams

似乎大家都比較喜歡 Lambda 和 Streams —— Nashorn 也是!雖然 ECMAScript 5.1 中缺少 Java 8 Lambda 表達(dá)式中的緊縮箭頭的語法,但我們可以在接受 Lambda 表達(dá)式的地方使用函數(shù)來替代。

 

  1. var list2 = new java.util.ArrayList(); 
  2. list2.add("ddd2"); 
  3. list2.add("aaa2"); 
  4. list2.add("bbb1"); 
  5. list2.add("aaa1"); 
  6. list2.add("bbb3"); 
  7. list2.add("ccc"); 
  8. list2.add("bbb2"); 
  9. list2.add("ddd1"); 
  10.  
  11. list2 
  12.     .stream() 
  13.     .filter(function(el) { 
  14.         return el.startsWith("aaa"); 
  15.     }) 
  16.     .sorted() 
  17.     .forEach(function(el) { 
  18.         print(el); 
  19.     }); 
  20.     // aaa1, aaa2 

擴(kuò)展類

Java 的類型可以簡(jiǎn)單的通過 Java.extend 進(jìn)行擴(kuò)展,在下個(gè)例子你將在腳本中創(chuàng)建一個(gè)多線程示例:

 

  1. var Runnable = Java.type('java.lang.Runnable'); 
  2. var Printer = Java.extend(Runnable, { 
  3.     run: function() { 
  4.         print('printed from a separate thread'); 
  5.     } 
  6. }); 
  7.  
  8. var Thread = Java.type('java.lang.Thread'); 
  9. new Thread(new Printer()).start(); 
  10.  
  11. new Thread(function() { 
  12.     print('printed from another thread'); 
  13. }).start(); 
  14.  
  15. // printed from a separate thread 
  16. // printed from another thread 

 

參數(shù)重載

方法和函數(shù)可以使用點(diǎn)符號(hào)或方括號(hào)來進(jìn)行調(diào)用。

 

  1. var System = Java.type('java.lang.System'); 
  2. System.out.println(10);              // 10 
  3. System.out["println"](11.0);         // 11.0 
  4. System.out["println(double)"](12);   // 12.0 

在使用重載的參數(shù)來調(diào)用方法時(shí)可以傳遞可選參數(shù)來確定具體調(diào)用了哪個(gè)方法,如 println(double)。

Java Beans

我們不需要常規(guī)的用 getter 或者 setter 來訪問類成員屬性,可直接用屬性名簡(jiǎn)單訪問 Java Bean 中的屬性。例如:

 

  1. var Date = Java.type('java.util.Date'); 
  2. var date = new Date(); 
  3. date.year += 1900
  4. print(date.year);  // 2014 

函數(shù)語法

如果只是簡(jiǎn)單的一行函數(shù)我們可以不用大括號(hào):

 

  1. function sqr(x) x * x; 
  2. print(sqr(3));    // 9 

屬性綁定

來自不同對(duì)象的屬性可以綁定在一起:

 

  1. function sqr(x) x * x; 
  2. print(sqr(3));    // 9 

 

字符串處理

我喜歡字符串裁剪.

  1. print(" hehe".trimLeft()); // hehe print("hehe ".trimRight() + "he"); // hehehe 

在哪里

以防忘記你在哪里:

  1. print(__FILE__, __LINE__, __DIR__); 

Import 的范圍

有時(shí),這在一次性導(dǎo)入多個(gè)java 包時(shí)非常有用。我們可以使用JavaImporter并結(jié)合with,在with塊范圍內(nèi)引用:

  1. var imports = new JavaImporter(java.io, java.lang); 
  2. with (imports) { 
  3.     var file = new File(__FILE__); 
  4.     System.out.println(file.getAbsolutePath()); 
  5.     // /path/to/my/script.js 

數(shù)組轉(zhuǎn)換

有些包時(shí)可以直接使用而不必利用 Java.type 或JavaImporter引入,如 java.util:

  1. var list = new java.util.ArrayList(); 
  2. list.add("s1"); list.add("s2"); list.add("s3"); 

如下的代碼演示了將java list轉(zhuǎn)換為JavaScript的數(shù)組:

  1. var jsArray = Java.from(list); 
  2. print(jsArray);                                  // s1,s2,s3 
  3. print(Object.prototype.toString.call(jsArray));  // [object Array] 

其他的方式:

  1. var javaArray = Java.to([35711], "int[]"); 

調(diào)用父類函數(shù)

在 JavaScript 中訪問重載的成員會(huì)有一點(diǎn)點(diǎn)尷尬,因?yàn)?ECMAScript 沒有類似 Java 的 super 關(guān)鍵字一樣的東西。所幸的是 Nashorn 有辦法解決。

首先我們?cè)?Java 代碼中定義一個(gè)超類:

  1. class SuperRunner implements Runnable { 
  2.     @Override 
  3.     public void run() { 
  4.         System.out.println("super run"); 
  5.     } 

接下來我們?cè)?JavaScript 中重載 SuperRunner 。創(chuàng)建一個(gè)新的 Runner 實(shí)例時(shí)請(qǐng)注意 Nashorn 的擴(kuò)展語法:其重載成員的語法是參考 Java 的匿名對(duì)象的做法。

 

  1. var SuperRunner = Java.type('com.winterbe.java8.SuperRunner'); 
  2. var Runner = Java.extend(SuperRunner); 
  3.  
  4. var runner = new Runner() { 
  5.     run: function() { 
  6.         Java.super(runner).run(); 
  7.         print('on my run'); 
  8.     } 
  9. runner.run(); 
  10.  
  11. // super run 
  12. // on my run 

我們使用Java.super調(diào)用了重載方法 SuperRunner.run()。

在JavaScript中執(zhí)行其它腳本是十分容易的。我們可以load函數(shù)載入本地或遠(yuǎn)程的腳本。

在我的很多web前端中都使用了 Underscore.js ,因此在Nashorn中我們可以重用 Underscore:

 

  1. load('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js'); 
  2.  
  3. var odds = _.filter([123456], function (num) { 
  4.     return num % 2 == 1
  5. }); 
  6. print(odds);  // 1, 3, 5

擴(kuò)展腳本的執(zhí)行是在同一個(gè) JavaScript 上下文中,因此我們可以直接訪問 underscore 變量。記住腳本的加載可能會(huì)因?yàn)樽兞棵闹丿B導(dǎo)致代碼出問題。

我們可以通過將加載的腳本文件放置到一個(gè)新的全局上下文來解決這個(gè)問題:

  1. loadWithNewGlobal('script.js'); 

命令行腳本

如果你對(duì)用 Java 編寫命令行腳本很感興趣的話,可以試試 Nake 。Nake 是一個(gè)為 Java 8 Nashorn 準(zhǔn)備的簡(jiǎn)單 Make 工具。你可以在 Nakefile 文件中定義任務(wù),然后使用 nake — myTask 來運(yùn)行任務(wù)。任務(wù)使用 JavaScript 編寫并通過 Nashorn 腳本模式運(yùn)行,因此你可以讓你的終端應(yīng)用完全利用 Java 8 API 和其他 Java 庫強(qiáng)大的功能。

對(duì) Java 開發(fā)者而言,編寫命令行腳本從來沒有如此簡(jiǎn)單過。

總結(jié)

我希望這篇文章對(duì)你有用,可以讓你輕松理解 Nashorn JavaScript 引擎。更多關(guān)于 Nashorn 的信息請(qǐng)閱讀 這里這里 和 這里. 如果你是要用 Nashorn 編寫 Shell 腳本的話可以參考 這里.

過去我也發(fā)表了一些 文章 是關(guān)于如何在 Nashron 引擎中使用 Backbone.js 模型數(shù)據(jù)的。如果你想要了解更多 Java 8 的話可以去看看我的文章 Java 8 Tutorial 和 Java 8 Stream Tutorial.

本文中的示例代碼可以通過 GitHub 獲取,你可以 fork 這個(gè)倉庫并通過 Twitter 來給我反饋。

堅(jiān)持編程!

責(zé)任編輯:張燕妮 來源: luke, coyee, CY2
相關(guān)推薦

2014-03-26 15:08:33

Java 8腳本引擎

2014-07-14 11:34:53

Java 8Nashorn

2009-04-04 09:42:53

IE8JScript瀏覽器

2013-12-06 10:12:49

Android游戲引擎libgdx教程

2014-07-15 14:48:26

Java8

2014-03-19 11:04:14

Java 8Java8特性

2009-06-30 11:33:55

腳本JSP教程

2018-08-28 16:02:59

LinuxShellBash

2022-05-30 10:31:34

Bash腳本Linux

2013-12-06 09:59:53

Android游戲引擎libgdx教程

2025-04-17 02:00:00

2011-07-18 11:07:12

iPhone 游戲 引擎

2011-07-18 10:53:09

2011-07-18 12:29:10

2011-07-18 11:39:58

iPhone 游戲 引擎

2011-07-18 11:23:29

iPhone 游戲 動(dòng)畫

2016-09-26 14:16:18

shell腳本bash

2011-08-29 15:10:19

JAVALua 腳本

2011-07-20 13:37:14

2009-12-28 10:16:48

Groovy 1.7
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)