Introduktion til Nashorn

1. Introduktion

Denne artikel er fokuseret på Nashorn - den nye standard JavaScript-motor til JVM fra og med Java 8.

Mange sofistikerede teknikker er blevet brugt til at fremstille Nashorn størrelsesordener mere performant end sin forgænger kaldte Næsehorn, så det er en værdifuld ændring.

Lad os se på nogle af de måder, hvorpå det kan bruges.

2. Kommandolinje

JDK 1.8 inkluderer en kaldet kommandolinjetolker jjs som kan bruges til at køre JavaScript-filer eller, hvis de startes uden argumenter, som en REPL (interaktiv shell):

$ $ JAVA_HOME / bin / jjs hello.js Hej verden

Her filen hej.js indeholder en enkelt instruktion: print (“Hello World”);

Den samme kode kan køres på den interaktive måde:

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

Du kan også instruere * nix runtime at bruge jjs til at køre et målscript ved at tilføje et #! $ JAVA_HOME / bin / jjs som første linje:

#! $ JAVA_HOME / bin / jjs var greeting = "Hej verden"; print (hilsen);

Og så kan filen køres som normalt:

$ ./hello.js Hello World

3. Indbygget scriptmotor

Den anden og sandsynligvis mere almindelige måde at køre JavaScript fra JVM på er via ScriptEngine. JSR-223 definerer et sæt scripting-API'er, der giver mulighed for en tilslutbar scriptmotorarkitektur, der kan bruges til ethvert dynamisk sprog (forudsat at det naturligvis har en JVM-implementering).

Lad os oprette en JavaScript-motor:

ScriptEngine engine = ny ScriptEngineManager (). GetEngineByName ("nashorn"); Objektresultat = engine.eval ("var hilsen =" hej verden ";" + "print (hilsen);" + "hilsen");

Her skaber vi et nyt ScriptEngineManager og straks bede den om at give os en ScriptEngine som hedder nashorn. Derefter sender vi et par instruktioner og opnår resultatet, som forudsigeligt viser sig at være et SnorHej Verden“.

4. Videregivelse af data til scriptet

Data kan overføres til motoren ved at definere en Bindinger objekt og videregive det som en anden parameter til eval fungere:

Bindingsbindinger = engine.createBindings (); bindings.put ("count", 3); bindings.put ("navn", "baeldung"); String script = "var greeting =" Hello ";" + "for (var i = count; i> 0; i--) {" + "hilsen + = navn + ''" + "}" + "hilsen"; Objekt bindingsResult = engine.eval (script, bindinger);

At køre dette uddrag producerer: “Hej baeldung baeldung baeldung“.

5. Påkald af JavaScript-funktioner

Det er selvfølgelig muligt at kalde JavaScript-funktioner fra din Java-kode:

engine.eval ("funktion composeGreeting (navn) {" + "returner 'Hej' + navn" + "}"); Invocable invocable = (Invocable) motor; Objekt funcResult = invocable.invokeFunction ("composeGreeting", "baeldung");

Dette vender tilbage “Hej baeldung“.

6. Brug af Java-objekter

Da vi kører i JVM, er det muligt at bruge native Java-objekter inden for JavaScript-kode.

Dette opnås ved hjælp af en Java objekt:

Objektkort = engine.eval ("var HashMap = Java.type ('java.util.HashMap');" + "var map = ny HashMap ();" + "map.put ('hej', 'verden')) ; "+" kort ");

7. Sprogudvidelser

Nashorn er målrettet mod ECMAScript 5.1 men det giver udvidelser for at gøre JavaScript-brug en smule pænere.

7.1. Itererende samlinger med For-Each

For hver er en praktisk udvidelse, der gør iteration over forskellige samlinger lettere:

String script = "var list = [1, 2, 3, 4, 5];" + "var resultat = '';" + "for hver (var i på listen) {" + "resultat + = i + '-';" + "};" + "udskriv (resultat);"; engine.eval (script);

Her slutter vi os til elementer i en matrix ved hjælp af for hver iterationskonstruktion.

Den resulterende output vil være 1-2-3-4-5-.

7.2. Funktionslitteratur

I enkle funktionserklæringer kan du udelade krøllede seler:

funktionsforøgelse (in) ++ in

Dette kan naturligvis kun gøres for enkle funktioner med én linie.

7.3. Betingede fangstklausuler

Det er muligt at tilføje beskyttede fangstklausuler, der kun udføres, hvis den angivne betingelse er sand:

prøv {kast "BOOM"; } fange (e hvis typeof e === 'streng') {print ("Streng kastet:" + e); } fange (e) {print ("dette burde ikke ske!"); }

Dette vil udskrive “Streng kastet: BOOM“.

7.4. Typede arrays og type konverteringer

Det er muligt at bruge Java-indtastede arrays og konvertere til og fra JavaScript-arrays:

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

Nashorn udfører nogle typekonverteringer her for at sikre, at alle værdierne fra det dynamisk typte JavaScript-array kan passe ind i Java-arrays, der kun er det heltal.

Resultatet af at kalde ovenfor fungerer med argument [100, “1654”, sandt] resulterer i output på 100, 1654 og 1 (alle tal).

Det Snor og boolske værdier blev implicit konverteret til deres logiske heltal modstykker.

7.5. Indstilling af objektets prototype med Object.setPrototypeOf

Nashorn definerer en API-udvidelse, der gør det muligt for os at ændre prototypen på et objekt:

Object.setPrototypeOf (obj, newProto)

Denne funktion betragtes generelt som et bedre alternativ til Object.prototype .__ proto__ så det skal være den foretrukne måde at indstille objektets prototype i al ny kode.

7.6. Magisk __noSuchProperty__ og __noSuchMethod__

Det er muligt at definere metoder på et objekt, der påberåbes, når som helst udefineret der er adgang til ejendom eller en udefineret metode påberåbes:

var demo = {__noSuchProperty__: function (propName) {print ("Adgang til ikke-eksisterende ejendom:" + propName); }, __noSuchMethod__: funktion (methodName) {print ("Påkaldt ikke-eksisterende metode:" + methodName); }}; demo.doesNotExist; demo.callNonExistingMethod ()

Dette vil udskrive:

Adgang til ikke-eksisterende ejendom: doesNotExist Påkaldt ikke-eksisterende metode: callNonExistingMethod

7.7. Bind objektegenskaber med Object.bindProperties

Object.bindProperties kan bruges til at binde egenskaber fra et objekt til et andet:

var first = {name: "Whisky", alder: 5}; var sekund = {volumen: 100}; Object.bindProperties (første, anden); print (første. volumen); sekund. volumen = 1000; print (første. volumen);

Bemærk, at dette skaber er en "live" binding, og eventuelle opdateringer til kildeobjektet er også synlige gennem bindingsmålet.

7.8. Placeringer

Aktuelt filnavn, bibliotek og en linje kan fås fra globale variabler __FILE__, __DIR__, __LINE__:

print (__ FILE__, __LINE__, __DIR__)

7.9. Udvidelser til String.prototype

Der er to enkle, men meget nyttige udvidelser, der Nashorn giver på Snor prototype. Disse er trimRight og trimVenstre funktioner, der ikke overraskende returnerer en kopi af Snor med det hvide område fjernet:

print ("hej verden" .trimLeft ()); print ("hej verden" .trimRight ());

Udskriver "hej verden" to gange uden at føre mellemrum.

7.10. Java.asJSONKompatibel Fungere

Ved hjælp af denne funktion kan vi få et objekt, der er kompatibelt med Java JSON-bibliotekets forventninger.

Nemlig at hvis det selv eller ethvert objekt, der kan nås gennem det, er et JavaScript-array, vil sådanne objekter blive eksponeret som JSObject der også implementerer Liste interface til eksponering af arrayelementerne.

Objekt obj = engine.eval ("Java.asJSONCompatible ({nummer: 42, hilsen: 'hej', primtal: [2,3,5,7,11,13]}"); Map map = (Map) obj; System.out.println (map.get ("hilsen")); System.out.println (map.get ("primer")); System.out.println (List.class.isAssignableFrom (map.get ("primes"). GetClass ()));

Dette vil udskrive “Hej" efterfulgt af [2, 3, 5, 7, 11, 13] efterfulgt af rigtigt.

8. Indlæsning af scripts

Det er også muligt at indlæse en anden JavaScript-fil inden for ScriptEngine:

load ('classpath: script.js')

Et script kan også indlæses fra en URL:

load ('/ script.js')

Husk, at JavaScript ikke har et begreb med navneområder, så alt bliver stablet ind i det globale omfang. Dette gør det muligt for indlæste scripts at skabe navngivningskonflikter med din kode eller hinanden. Dette kan afhjælpes ved hjælp af loadWithNewGlobal fungere:

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

Med følgende math_module.js:

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

Her definerer vi et navngivet objekt matematik der har en enkelt funktion kaldet stigning. Ved hjælp af dette paradigme kan vi endda efterligne grundlæggende modularitet!

8. Konklusion

Denne artikel udforskede nogle af funktionerne i Nashorn JavaScript-motor. Eksempler, der vises her, brugte bogstavelige scripts, men for virkelige scenarier vil du sandsynligvis beholde dit script i separate filer og indlæse dem ved hjælp af en Læser klasse.

Som altid er koden i denne opskrivning tilgængelig på GitHub.