1前書き

この記事は、Java 8以降のJVMの新しいデフォルトJavaScriptエンジンである__Nashornに焦点を当てています。

多くの洗練されたテクニックがその前身の

Rhinoと比べて

Nashorn__桁の性能を上げるために使われてきたので、それは価値のある変更です。

それが使用されることができる方法のいくつかを見てみましょう。


2コマンドライン

JDK 1.8には、

jjs

というコマンドラインインタプリタが含まれています。これは、JavaScriptファイルを実行するため、または引数なしで起動した場合はREPL(対話型シェル)として使用できます。

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

ここで、ファイル

hello.js

には、単一の命令が含まれています。

print(“ Hello World”);

同じコードを対話的に実行することができます。

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

最初の行に

#!$ JAVA

HOME/bin/jjs

を追加して、ターゲットスクリプトを実行するために

jjs__を使用するように** nixランタイムに指示することもできます。

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

そしてファイルは通常通りに実行することができます。

$ ./hello.js
Hello World


3埋め込みスクリプトエンジン

JVM内からJavaScriptを実行する2番目の、そしておそらくもっと一般的な方法は__ScriptEngineを使うことです。 JVMの実装、もちろん)。

JavaScript ____エンジンを作成しましょう。

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");

Object result = engine.eval(
   "var greeting='hello world';" +
   "print(greeting);" +
   "greeting");

ここでは新しい

ScriptEngineManager

を作成し、すぐにそれに

nashorn

という名前の

ScriptEngine

を渡すように依頼します。それから、いくつかの命令を渡して、予想どおり

String



hello world

“であることが判明した結果を取得します。


4スクリプトにデータを渡す


Bindings

オブジェクトを定義し、それを2番目のパラメーターとして

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);

このスニペットを実行すると、「

こんにちはbaeldung baeldung baeldung

」となります。


5 JavaScript関数を呼び出す

もちろん、あなたのJavaコードからJavaScriptの関数を呼び出すことも可能です。

engine.eval("function composeGreeting(name) {" +
  "return 'Hello ' + name" +
  "}");
Invocable invocable = (Invocable) engine;

Object funcResult = invocable.invokeFunction("composeGreeting", "baeldung");

これは“

Hello baeldung

”を返します。


6. Javaオブジェクトの使い方

我々はJVM内で実行しているので、JavaScriptコード内からネイティブJavaオブジェクトを使用することが可能です。

これは

Java

オブジェクトを使用して実現されます。

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


7. 言語拡張機能


  • Nashorn

    はECMAScript 5.1 ** をターゲットとしていますが、JavaScriptの使用を少し良くするための拡張機能を提供します。


7.1. for-each

を使用してコレクションを繰り返す


For-each

は、さまざまなコレクションの反復処理を簡単にするための便利な拡張機能です。

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

engine.eval(script);

ここでは、forfor each__ iteration構文を使用して配列の要素を結合します。

結果の出力は

1-2-3-4-5-

になります。


7.2. 関数リテラル

単純な関数宣言では、中括弧を省略することができます。

function increment(in) ++in

明らかに、これは単純なワンライナー関数に対してのみ行うことができます。


7.3. 条件付きキャッチ句

指定した条件が真の場合にのみ実行される、保護されたcatch句を追加することができます。

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

これにより、「

String thrown:BOOM

」と表示されます。


7.4. 型付き配列と型変換

Java型の配列を使用したり、JavaScriptの配列との間で変換したりすることができます。

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


Nashorn

は、動的に型指定されたJavaScript配列のすべての値が整数のみのJava配列に収まるように、ここでいくつかの型変換を実行します。

引数

[100、“ 1654”、true]

を指定して上記の関数を呼び出した結果、100、1654、および1(すべての数値)が出力されます。


String

とブール値は、暗黙のうちに論理整数値に変換されました。


7.5.

Object.setPrototypeOf


を使用してオブジェクトのプロトタイプを設定する


Nashorn

は、オブジェクトのプロトタイプを変更できるようにするAPI拡張を定義します。

Object.setPrototypeOf(obj, newProto)

この関数は一般的に

Object.prototype .


proto

____に代わるより良い代替手段と考えられているので、すべての新しいコードでオブジェクトのプロトタイプを設定するのが好ましい方法です。


7.6. 魔法



_noSuchProperty








_ noSuchMethod






undefined

プロパティがアクセスされたとき、または

undefined

メソッドが呼び出されたときに必ず呼び出されるメソッドをオブジェクトに定義することができます。

var demo = {
    ____noSuchProperty____: function (propName) {
        print("Accessed non-existing property: " + propName);
    },

    ____noSuchMethod____: function (methodName) {
        print("Invoked non-existing method: " + methodName);
    }
};

demo.doesNotExist;
demo.callNonExistingMethod()

これは印刷されます:

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


7.7. オブジェクトプロパティを

Object.bindProperties


でバインドする


Object.bindProperties

を使用して、あるオブジェクトから別のオブジェクトにプロパティをバインドできます。

var first = {
    name: "Whiskey",
    age: 5
};

var second = {
    volume: 100
};

Object.bindProperties(first, second);

print(first.volume);

second.volume = 1000;
print(first.volume);

これは「ライブ」バインディングであり、ソースオブジェクトに対する更新もバインディングターゲットを通して見ることができます。


7.8. 場所

現在のファイル名、ディレクトリ、および行は、グローバル変数



_FILE

_




DIR





LINE

__から取得できます。

print(____FILE____, ____LINE____, ____DIR____)


7.9. String.prototype

の拡張


Nashorn



String

プロトタイプに対して提供する、2つの単純で非常に便利な拡張があります。これらは

trimRight



trimLeft

関数で、驚くことに、空白を削除した

String

のコピーを返します。

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

前後にスペースを入れずに「hello world」を2回印刷します。


7.10. __Java.asJSON互換性のある機能

この関数を使うことで、Java JSONライブラリの期待と互換性のあるオブジェクトを取得することができます。

つまり、それ自体、またはそれを通して推移的に到達可能な任意のオブジェクトがJavaScript配列の場合、そのようなオブジェクトは

JSObject

として公開されます。これは、配列要素を公開するための

List

インターフェースも実装します。

Object obj = engine.eval("Java.asJSONCompatible(
  { number: 42, greet: 'hello', primes:[2,3,5,7,11,13]})");
Map<String, Object> map = (Map<String, Object>)obj;

System.out.println(map.get("greet"));
System.out.println(map.get("primes"));
System.out.println(List.class.isAssignableFrom(map.get("primes").getClass()));

これにより、“

hello

”の後に

[2、3、5、7、11、13]

の後に__trueが続きます。

8.スクリプトを読み込む


ScriptEngine

内から別のJavaScriptファイルを読み込むこともできます。

load('classpath:script.js')

スクリプトはURLからロードすることもできます。

load('/script.js')

JavaScriptには名前空間の概念がないため、すべてがグローバルスコープに組み込まれていることに注意してください。これにより、ロードされたスクリプトがあなたのコードまたはお互いに命名の衝突を引き起こすことが可能になります。これは、

loadWithNewGlobal

関数を使用して軽減できます。

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

次の

math

module.js__を使って:

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

math;bai

ここでは

incrementという単一の関数を持つ

math__という名前のオブジェクトを定義しています。


8結論

この記事では、

__ Nashorn J


avaScriptエンジンのいくつかの機能について説明しました。ここで紹介する例では文字列リテラルスクリプトを使用しましたが、実際のシナリオでは、スクリプトを別々のファイルに保存して

Reader__クラスを使用してロードすることをお勧めします。

いつものように、この記事のコードはすべてhttps://github.com/eugenp/tutorials/tree/master/core-java[over on Github]です。