【JDK8】JavaScript 引擎 Nashorn 之 jjs


在安裝完 JDK8,並設定好 PATH 中包括 JDK 的 bin 目錄之後,我們來寫每個程式語言都會練習的第一個程式 “Hello, World”,首先,請新增一個純文字檔案,將檔案名稱改為 hello.js(別忘了你要看得到副檔名),然後編輯檔案內容:

print('Hello, World');

看!第一個 Java 程式就是這麼簡單,接著開啟命令列,切換到 hello.js 所在目錄,使用 jjs 指令來執行程式:

> jjs hello.js
Hello, World

(這不是 Java 啊!啊!啊!啊!… XD)

被識破了嗎??JDK8 包括了一個新的 JavaScript 引擎 Nashorn,你可以使用命令列工具 jjs 來運行 JavaScript 程式,這包括在 JDK 的 bin 目錄中,你也可以在 Java 程式中嵌入 Nashorn 引擎。

這篇文章,要先來談談,命令列工具 jjs 可以如何執行 JavaScript 程式。你可以直接鍵入互動模式,測試一些簡單的 JavaScript:

>jjs
jjs> print('Hello, World')
Hello, World
jjs> 1 + 2
3
jjs> [1, 2, 3].join(', ')
1, 2, 3
jjs> exit()

如果要在目前 .js 檔案中,載入其他 .js 檔案,可以使用 load,可以是本機或是遠端主機上的檔案。例如:

load('https://openhome.cc/Gossip/JavaScript/samples/js/gossip-0.1.js');
XD.each(['a', 'b', 'c'], function(elem) {
    print(elem);
});

嚴格模式

Nashorn 引擎是基於 ECMAScript 5.1,未來的 Nashorn 版本可望支援 ECMAScript 6,因而基本上,在〈JavaScript 語言核心〉 系列中介紹到 ECMAScript 5,Nashorn 都可以支援,像是 嚴格模式,你也可以在執行 jjs 指令時加上 -strict 表示使用一律嚴格模式(預設不啟用嚴格模式),而不用在程式碼中使用 'use strict' 字串。

例如,如果你有個 hello.js 如下:

name = 'Justin';
print('Hello, ' + name);

如下執行時就會發生錯誤,因為 ECMAScript 5 嚴格模式下,變數必須使用 var 宣告:

>jjs -strict hello.js
hello.js:1 ReferenceError: "name" is not defined

scripting 模式

執行 jjs 指令時如果加上 -scripting,可以使用 Nashorn 本身提供(而 JavaScript 本身沒有)的一些特性。

例如,JavaScript 本身對 ''"" 來建立字串並沒有區別,如果加上了 -scripting"" 包括的字串中若有 ${expr},expr 將會被執行,'' 則不會做任何處理。例如:

var name = 'Justin';
print('Hello, ${name}');
print("Hello, ${name}");
print("Hello, ${name.toUpperCase()}");   

執行結果如下:

>jjs -scripting hello.js
Hello, ${name}
Hello, Justin
Hello, JUSTIN

scripting 模式下,多了一些全域變數可以使用,例如,可以使用 $ARG 取得命令列引數:

$ARG.forEach(function(elem, index) {
    print("index: ${index}, elem: ${elem}");
});

指定命令列引數時,除了加上 -scripting 外,命令列引數必須銜接在 -- 之後。例如:

>jjs -scripting hello.js -- 1 2 3
index: 0, elem: 1
index: 1, elem: 2
index: 2, elem: 3

在不使用 -scripting 的情況下,也可以使用 arguments 來取得命令列引數。

在加上 -scripting 的情況下,$ENV 可用來取得一些環境變數。例如:

for(var prop in $ENV) {
    print("${prop}: ${$ENV[prop]}");
}

以下是一個執行範例與結果顯示片段:

>jjs -scripting hello.js
USERDOMAIN_ROAMINGPROFILE: Justin-2012
LOCALAPPDATA: C:\Users\Justin\AppData\Local
PROCESSOR_LEVEL: 6
FP_NO_HOST_CHECK: NO
USERDOMAIN: Justin-2012
LOGONSERVER: \\JUSTIN-2012
PROMPT: $P$G
SESSIONNAME: Console
略... 

如果想執行外部指令,可以使用 $EXEC,例如,$EXEC('notepad') 將會開啟記事本。如果你不喜歡使用 print,那在 -script 下可以使用 echo 代替,如果想提示訊息並取得使用者輸入,可以使用 readLine 方法,使用 readFully 則可以取得檔案內容:

var fileName = readLine('Input a filename: ');
var content = readFully(fileName);
echo(content);

執行結果範例如下:

>jjs -scripting hello.js
Input a filename: hello.js
var fileName = readLine('Input a filename: ');
var content = readFully(fileName);
echo(content);

-scripting 模式下,可以使用 # 作為註解,如果 # 出現在原始碼的第一行,不用加上 -scripting,也會自動進入 scripting 模式。

scripting 模式下,可以撰寫 HereDoc,也就是多行字串,直接來看個範例就知道怎麼撰寫:

#
var doc = <<DOC
Hello, ${arguments[0]}.
This is heredoc example.
Try it!
DOC

print(doc);

<< 表示開始一個 HereDoc,DOC 指定了一個識別名稱,作為多行字串結束標記,標記名稱可以自訂,HereDoc 中可以直譯字串。一個執行結果如下:

>jjs hello.js -- Justin
Hello, Justin.
This is heredoc example.
Try it!

更多 Nashorn 的 scripting 模式說明,還可以參考〈Nashorn and Shell Scripting〉

jjs 是為 Nashorn 量身打造的指令,如果想與 JSR223 相容,可以使用 jrunscript,詳情可察看〈Invoking Nashorn from the Command Line〉