Рубрики
Без рубрики

Введение в Нашорн

Краткое практическое введение в Nashorn, движок JavaScript по умолчанию для JVM начиная с Java 8.

Автор оригинала: baeldung.

1. введение

Эта статья посвящена Nashorn – новому движку JavaScript по умолчанию для JVM начиная с Java 8.

Многие сложные методы были использованы для того, чтобы сделать Нашорн на порядки более производительным, чем его предшественник под названием Носорог, так что это достойное изменение.

Давайте рассмотрим некоторые из способов, которыми его можно использовать.

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

Вы также можете указать среде выполнения *nix использовать js для запуска целевого сценария, добавив #!$JAVA_HOME/bin/jjs в качестве первой строки:

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

И тогда файл может быть запущен в обычном режиме:

$ ./hello.js
Hello World

3. Встроенный движок Сценариев

Второй и, вероятно, более распространенный способ запуска JavaScript из JVM-это через Script Engine. JSR-223 определяет набор API-интерфейсов сценариев, позволяющих использовать подключаемую архитектуру движка сценариев, которая может использоваться для любого динамического языка (при условии, конечно, что он имеет реализацию JVM).

Давайте создадим движок JavaScript:

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

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

Здесь мы создаем новый ScriptEngineManager и сразу же просим его предоставить нам Движок сценариев с именем nashorn . Затем мы передаем пару инструкций и получаем результат, который, как и следовало ожидать, оказывается строкой hello world “.

4. Передача данных в скрипт

Данные могут быть переданы в механизм, определив объект Привязки и передав его в качестве второго параметра функции 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);

Running this snippet produces: ” Hello baeldung baeldung baeldung//”.

5. Вызов функций JavaScript

Конечно, можно вызывать функции JavaScript из вашего кода Java:

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

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

Это вернет ” Hello baeldung “.

6. Использование объектов Java

Поскольку мы работаем в JVM, можно использовать собственные объекты Java из кода JavaScript.

Это достигается с помощью объекта 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);

Здесь мы соединяем элементы массива с помощью конструкции for-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!");
}

Это выведет ” Брошенная строка: 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 и boolean были неявно преобразованы в их логические целочисленные аналоги.

7.5. Настройка Прототипа Объекта С Помощью Object.setPrototypeOf

Nashorn определяет расширение API, которое позволяет нам изменять прототип объекта:

Object.setPrototypeOf(obj, newProto)

Эта функция обычно считается лучшей альтернативой Object.prototype.__proto__ , поэтому она должна быть предпочтительным способом установки прототипа объекта во всем новом коде.

7.6. Магический __нет Такого Свойства__ и __нет Такого Метода__

Можно определить методы для объекта, который будет вызываться при каждом обращении к свойству 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 . Это функции trimRight и trimLeft , которые, что неудивительно, возвращают копию строки | с удаленными пробелами:

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

Дважды напечатает “hello world” без начальных и конечных пробелов.

7.10. Java.as Функция, совместимая с JSON

Используя эту функцию, мы можем получить объект, совместимый с ожиданиями библиотек Java JSON.

А именно, что если он сам или любой объект, транзитивно доступный через него, является массивом JavaScript, то такие объекты будут отображаться как JSObject , который также реализует интерфейс List для отображения элементов массива.

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

Это выведет ” привет “, за которым последует [2, 3, 5, 7, 11, 13] за ним следует true.

8. Загрузка скриптов

Также можно загрузить другой файл JavaScript из Script Engine :

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

Здесь мы определяем объект с именем math , который имеет одну функцию с именем increment. Используя эту парадигму, мы можем даже эмулировать базовую модульность!

8. Заключение

В этой статье были рассмотрены некоторые особенности движка Nashorn J javascript. В приведенных здесь примерах использовались сценарии строковых литералов, но для реальных сценариев вы, скорее всего, захотите сохранить свой сценарий в отдельных файлах и загрузить их с помощью класса Reader .

Как всегда, код в этой записи полностью доступен на GitHub .