Inleiding tot Nashorn

1. Inleiding

Dit artikel is gericht op Nashorn - de nieuwe standaard JavaScript-engine voor de JVM vanaf Java 8.

Er zijn veel geavanceerde technieken gebruikt om te maken Nashorn ordes van grootte performanter dan zijn voorganger noemde Neushoorn, dus het is een verandering die de moeite waard is.

Laten we eens kijken naar enkele manieren waarop het kan worden gebruikt.

2. Commandoregel

JDK 1.8 bevat een opdrachtregelinterpreter genaamd jjs die kan worden gebruikt om JavaScript-bestanden uit te voeren of, indien gestart zonder argumenten, als een REPL (interactieve shell):

$ $ JAVA_HOME / bin / jjs hello.js Hallo wereld

Hier het bestand hallo.js bevat een enkele instructie: print ("Hallo wereld");

Dezelfde code kan op de interactieve manier worden uitgevoerd:

$ $ JAVA_HOME / bin / jjs jjs> print ("Hallo wereld") Hallo wereld

U kunt ook de * nix-runtime opdracht geven om te gebruiken jjs voor het uitvoeren van een doelscript door een #! $ JAVA_HOME / bin / jjs als de eerste regel:

#! $ JAVA_HOME / bin / jjs var greeting = "Hallo wereld"; print (groet);

En dan kan het bestand zoals normaal worden uitgevoerd:

$ ./hello.js Hallo wereld

3. Ingebouwde script-engine

De tweede en waarschijnlijk meer gebruikelijke manier om JavaScript vanuit de JVM uit te voeren, is via de ScriptEngine. JSR-223 definieert een set scripting-API's, die een pluggable script-engine-architectuur mogelijk maken die kan worden gebruikt voor elke dynamische taal (mits deze een JVM-implementatie heeft, natuurlijk).

Laten we een JavaScript-engine maken:

ScriptEngine-engine = nieuwe ScriptEngineManager (). GetEngineByName ("nashorn"); Object resultaat = engine.eval ("var groet =" hallo wereld ";" + "print (groet);" + "groet");

Hier maken we een nieuw ScriptEngineManager en vraag hem onmiddellijk om ons een ScriptEngine genaamd nashorn. Vervolgens geven we een paar instructies door en verkrijgen het resultaat dat voorspelbaar a blijkt te zijn DraadHallo Wereld“.

4. Gegevens doorgeven aan het script

Gegevens kunnen in de engine worden doorgegeven door een Bindingen object en het doorgeven als een tweede parameter aan het eval functie:

Bindingen bindingen = engine.createBindings (); bindings.put ("count", 3); bindings.put ("naam", "baeldung"); String script = "var greeting =" Hallo ";" + "for (var i = count; i> 0; i--) {" + "begroeting + = naam + ''" + "}" + "begroeting"; Object bindingsResult = engine.eval (script, bindingen);

Het uitvoeren van dit fragment levert het volgende op: "Hallo baeldung baeldung baeldung“.

5. Het aanroepen van JavaScript-functies

Het is natuurlijk mogelijk om JavaScript-functies aan te roepen vanuit uw Java-code:

engine.eval ("function composeGreeting (naam) {" + "return 'Hallo' + naam" + "}"); Invocable invocable = (Invocable) engine; Object funcResult = invocable.invokeFunction ("composeGreeting", "baeldung");

Dit zal 'Hallo baeldung“.

6. Java-objecten gebruiken

Omdat we in de JVM werken, is het mogelijk om native Java-objecten te gebruiken vanuit JavaScript-code.

Dit wordt bereikt door een Java voorwerp:

Object map = engine.eval ("var HashMap = Java.type ('java.util.HashMap');" + "var map = nieuwe HashMap ();" + "map.put ('hallo', 'wereld') ; "+" kaart ");

7. Taaluitbreidingen

Nashorn richt zich op ECMAScript 5.1 maar het biedt wel extensies om het gebruik van JavaScript een beetje leuker te maken.

7.1. Collecties herhalen met For-Each

Voor elk is een handige extensie om iteratie over verschillende collecties gemakkelijker te maken:

String script = "var list = [1, 2, 3, 4, 5];" + "var result = '';" + "voor elk (var i in lijst) {" + "resultaat + = i + '-';" + "};" + "print (resultaat);"; engine.eval (script);

Hier voegen we elementen van een array samen met behulp van voor elk iteratie construct.

De resulterende output zal zijn 1-2-3-4-5-.

7.2. Function Literals

In eenvoudige functieverklaringen kunt u accolades weglaten:

functie increment (in) ++ in

Dit kan uiteraard alleen worden gedaan voor eenvoudige, one-liner-functies.

7.3. Voorwaardelijke vangstclausules

Het is mogelijk om bewaakte catch-clausules toe te voegen die alleen worden uitgevoerd als de opgegeven voorwaarde waar is:

probeer {gooi "BOOM"; } catch (e if typeof e === 'string') {print ("String gegooid:" + e); } catch (e) {print ("dit mag niet gebeuren!"); }

Dit zal 'String gegooid: BOOM“.

7.4. Getypte arrays en typeconversies

Het is mogelijk om door Java getypeerde arrays te gebruiken en om van en naar JavaScript-arrays te converteren:

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

Nashorn voert hier een aantal typeconversies uit om ervoor te zorgen dat alle waarden van de dynamisch getypeerde JavaScript-array passen in de Java-arrays met alleen gehele getallen.

Het resultaat van het aanroepen van bovenstaande functie met argument [100, "1654", true] resulteert in de output van 100, 1654 en 1 (alle getallen).

De Draad en booleaanse waarden werden impliciet geconverteerd naar hun logische integer-tegenhangers.

7.5. Prototype van object instellen met Object.setPrototypeOf

Nashorn definieert een API-extensie waarmee we het prototype van een object kunnen wijzigen:

Object.setPrototypeOf (obj, newProto)

Deze functie wordt algemeen beschouwd als een beter alternatief voor Object.prototype .__ proto__ dus het zou de beste manier moeten zijn om het prototype van het object in alle nieuwe code in te stellen.

7.6. Magisch __noSuchProperty__ en __noSuchMethod__

Het is mogelijk om methoden op een object te definiëren die worden aangeroepen wanneer een ongedefinieerd eigendom wordt betreden of een ongedefinieerd methode wordt aangeroepen:

var demo = {__noSuchProperty__: function (propName) {print ("Toegang tot niet-bestaande eigenschap:" + propName); }, __noSuchMethod__: function (methodName) {print ("aangeroepen niet-bestaande methode:" + methodName); }}; demo.doesNotExist; demo.callNonExistingMethod ()

Dit zal afdrukken:

Toegang tot niet-bestaande eigenschap: doesNotExist aangeroepen niet-bestaande methode: callNonExistingMethod

7.7. Bind objecteigenschappen met Object.bindProperties

Object.bindProperties kan worden gebruikt om eigenschappen van het ene object aan het andere te binden:

var first = {naam: "Whisky", leeftijd: 5}; var second = {volume: 100}; Object.bindProperties (eerste, tweede); print (eerste.volume); tweede.volume = 1000; print (eerste.volume);

Merk op dat hierdoor een "live" binding ontstaat en dat eventuele updates van het bronobject ook zichtbaar zijn via het bindende doel.

7.8. Locaties

De huidige bestandsnaam, directory en een regel kunnen worden verkregen uit globale variabelen __BESTAND__, __DIR__, __LINE__:

afdrukken (__ FILE__, __LINE__, __DIR__)

7.9. Uitbreidingen voor String.prototype

Er zijn twee eenvoudige, maar zeer nuttige extensies die Nashorn biedt op de Draad voorlopig ontwerp. Dit zijn trimRight en trimLeft functies die, niet verrassend, een kopie van het Draad met de witruimte verwijderd:

print ("hallo wereld" .trimLeft ()); print ("hallo wereld" .trimRight ());

Zal tweemaal "hallo wereld" afdrukken zonder voorloop- of volgspaties.

7.10. Java.asJSONCompatibel Functie

Met behulp van deze functie kunnen we een object verkrijgen dat compatibel is met de verwachtingen van Java JSON-bibliotheken.

Namelijk dat als het zelf, of een ander object dat tijdelijk bereikbaar is, een JavaScript-array is, dergelijke objecten worden weergegeven als JSObject dat implementeert ook de Lijst interface voor het blootleggen van de array-elementen.

Object obj = engine.eval ("Java.asJSONCompatible ({nummer: 42, begroet: 'hallo', priemgetallen: [2,3,5,7,11,13]})"); Map map = (Map) obj; System.out.println (map.get ("greet")); System.out.println (map.get ("priemgetallen")); System.out.println (List.class.isAssignableFrom (map.get ("priemgetallen"). GetClass ()));

Dit zal 'Hallo" gevolgd door [2, 3, 5, 7, 11, 13] gevolgd door waar.

8. Scripts laden

Het is ook mogelijk om een ​​ander JavaScript-bestand te laden vanuit het ScriptEngine:

load ('classpath: script.js')

Een script kan ook worden geladen vanaf een URL:

load ('/ script.js')

Houd er rekening mee dat JavaScript geen concept van naamruimten heeft, dus alles wordt opgestapeld in het globale bereik. Dit maakt het mogelijk dat geladen scripts naamconflicten veroorzaken met uw code of met elkaar. Dit kan worden beperkt door de loadWithNewGlobal functie:

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

Met het volgende math_module.js:

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

Hier definiëren we een object met de naam wiskunde dat heeft een enkele functie genaamd toename. Met behulp van dit paradigma kunnen we zelfs basismodulariteit nabootsen!

8. Conclusie

In dit artikel zijn enkele kenmerken van het Nashorn J.avaScript-engine. Voorbeelden die hier worden getoond, gebruikten letterlijke tekenreeksscripts, maar voor scenario's uit het echte leven wilt u uw script waarschijnlijk in afzonderlijke bestanden bewaren en ze laden met een Lezer klasse.

Zoals altijd is de code in dit artikel overal beschikbaar op GitHub.