Introduksjon til Nashorn

1. Introduksjon

Denne artikkelen er fokusert på Nashorn - den nye standard JavaScript-motoren for JVM fra og med Java 8.

Mange sofistikerte teknikker har blitt brukt til å lage Nashorn størrelsesordener mer performant enn forgjengeren kalte Neshorn, så det er en verdig forandring.

La oss ta en titt på noen av måtene den kan brukes på.

2. Kommandolinje

JDK 1.8 inkluderer en kalt kommandolinjetolk jjs som kan brukes til å kjøre JavaScript-filer eller, hvis de startes uten argumenter, som et REPL (interaktivt skall):

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

Her filen hallo.js inneholder en enkelt instruksjon: utskrift (“Hello World”);

Den samme koden kan kjøres på en interaktiv måte:

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

Du kan også instruere * nix kjøretid å bruke jjs for å kjøre et målskript ved å legge til et #! $ JAVA_HOME / bin / jjs som første linje:

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

Og så kan filen kjøres som vanlig:

$ ./hello.js Hello World

3. Innebygd skriptmotor

Den andre og sannsynligvis vanligste måten å kjøre JavaScript fra JVM er via ScriptEngine. JSR-223 definerer et sett med skript-API-er, og muliggjør en pluggbar skriptmotorarkitektur som kan brukes til ethvert dynamisk språk (forutsatt at det har en JVM-implementering, selvfølgelig).

La oss lage en JavaScript-motor:

ScriptEngine engine = new ScriptEngineManager (). GetEngineByName ("nashorn"); Objektresultat = engine.eval ("var hilsen =" hei verden ";" + "utskrift (hilsen);" + "hilsen");

Her skaper vi et nytt ScriptEngineManager og ber den umiddelbart gi oss en ScriptEngine heter nashorn. Deretter gir vi et par instruksjoner og får resultatet som forutsigbart viser seg å være et StringHei Verden“.

4. Overføring av data til skriptet

Data kan overføres til motoren ved å definere en Bindinger objektet og sender det som en andre parameter til eval funksjon:

Bindinger bindinger = 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 (skript, bindinger);

Å kjøre denne kodebiten gir: “Hei baeldung baeldung baeldung“.

5. Påkalle JavaScript-funksjoner

Det er selvfølgelig mulig å ringe JavaScript-funksjoner fra Java-koden din:

engine.eval ("function composeGreeting (name) {" + "return 'Hello' + name" + "}"); Invocable invocable = (Invocable) motor; Objekt funcResult = invocable.invokeFunction ("composeGreeting", "baeldung");

Dette vil returnere “Hei baeldung“.

6. Bruke Java-objekter

Siden vi kjører i JVM er det mulig å bruke native Java-objekter fra JavaScript-koden.

Dette oppnås ved å bruke en Java gjenstand:

Objektkart = engine.eval ("var HashMap = Java.type ('java.util.HashMap');" + "var map = ny HashMap ();" + "map.put ('hallo', 'verden')) ; "+" kart ");

7. Språkutvidelser

Nashorn retter seg mot ECMAScript 5.1 men det gir utvidelser for å gjøre JavaScript-bruk litt hyggeligere.

7.1. Iterere samlinger med For-Each

For hver er en praktisk utvidelse for å gjøre iterasjon over forskjellige samlinger enklere:

Strengeskript = "var list = [1, 2, 3, 4, 5];" + "var resultat = '';" + "for hver (var i i listen) {" + "resultat + = i + '-';" + "};" + "utskrift (resultat);"; engine.eval (skript);

Her blir vi sammen med elementer i en matrise ved å bruke for hver iterasjonskonstruksjon.

Resultatet blir 1-2-3-4-5-.

7.2. Funksjonslitteratur

I enkle funksjonserklæringer kan du utelate krøllete bukseseler:

funksjonsøkning (in) ++ in

Åpenbart kan dette bare gjøres for enkle funksjoner med en linje.

7.3. Betingede fangstklausuler

Det er mulig å legge til beskyttede fangstklausuler som bare utføres hvis den angitte tilstanden er oppfylt:

prøv {kast "BOOM"; } catch (e if typeof e === 'string') {print ("Streng kastet:" + e); } fange (e) {print ("dette skulle ikke skje!"); }

Dette vil skrive ut “String kastet: BOOM“.

7.4. Typede matriser og typekonverteringer

Det er mulig å bruke Java-typede matriser og konvertere til og fra JavaScript-matriser:

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

Nashorn utfører noen typekonverteringer her for å sikre at alle verdiene fra den dynamisk typede JavaScript-matrisen kan passe inn i Java-matriser som bare er heltall.

Resultatet av å ringe over fungerer med argument [100, “1654”, sant] resulterer i produksjonen på 100, 1654 og 1 (alle tall).

De String og boolske verdier ble implisitt konvertert til deres logiske heltall-kolleger.

7.5. Setter objektets prototype med Object.setPrototypeOf

Nashorn definerer en API-utvidelse som gjør det mulig for oss å endre prototypen til et objekt:

Object.setPrototypeOf (obj, newProto)

Denne funksjonen anses generelt som et bedre alternativ til Object.prototype .__ proto__ så det bør være den foretrukne måten å sette objektets prototype i all ny kode.

7.6. Magisk __noSuchProperty__ og __noSuchMethod__

Det er mulig å definere metoder på et objekt som vil bli påkalt når et udefinert eiendommen er tilgjengelig eller en udefinert metoden påkalles:

var demo = {__noSuchProperty__: function (propName) {print ("Åpnet ikke-eksisterende eiendom:" + propName); }, __noSuchMethod__: function (methodName) {print ("Påkalt ikke-eksisterende metode:" + methodName); }}; demo.doesNotExist; demo.callNonExistingMethod ()

Dette vil skrive ut:

Tilgang til ikke-eksisterende eiendom: doesNotExist Påkalt ikke-eksisterende metode: callNonExistingMethod

7.7. Bind objektegenskaper med Object.bindProperties

Object.bindProperties kan brukes til å binde egenskaper fra ett objekt til et annet:

var først = {navn: "Whisky", alder: 5}; var sekund = {volum: 100}; Object.bindProperties (første, andre); skrive ut (første. volum); sekund. volum = 1000; skrive ut (første. volum);

Legg merke til at dette skaper er en "live" binding og eventuelle oppdateringer til kildeobjektet er også synlige gjennom bindingsmålet.

7.8. Steder

Nåværende filnavn, katalog og en linje kan fås fra globale variabler __FIL__, __DIR__, __LINE__:

skriv ut (__ FILE__, __LINE__, __DIR__)

7.9. Utvidelser til String.prototype

Det er to enkle, men veldig nyttige utvidelser som Nashorn gir på String prototype. Disse er trimRight og trimVenster funksjoner som, ikke overraskende, returnerer en kopi av String med tomrom fjernet:

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

Skriver ut "hallo verden" to ganger uten ledende eller etterfølgende mellomrom.

7.10. Java.asJSONKompatibel Funksjon

Ved hjelp av denne funksjonen kan vi skaffe et objekt som er kompatibelt med Java JSON-bibliotekets forventninger.

Nemlig at hvis det selv, eller et hvilket som helst objekt som kan nås gjennom det, er en JavaScript-matrise, vil slike objekter bli eksponert som JSObject som også implementerer Liste grensesnitt for å eksponere matriseelementene.

Objekt obj = engine.eval ("Java.asJSONCompatible ({nummer: 42, hilsen: 'hallo', primtall: [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 skrive ut “Hallo" etterfulgt av [2, 3, 5, 7, 11, 13] etterfulgt av ekte.

8. Laste inn skript

Det er også mulig å laste inn en annen JavaScript-fil fra ScriptEngine:

load ('classpath: script.js')

Et skript kan også lastes inn fra en URL:

last ('/ script.js')

Husk at JavaScript ikke har et begrep med navnerom, så alt blir stablet inn i det globale omfanget. Dette gjør det mulig for lastede skript å skape navnekonflikter med koden din eller hverandre. Dette kan avbøtes ved å bruke loadWithNewGlobal funksjon:

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

Med følgende math_module.js:

var matematikk = {inkrement: function (num) {return ++ num; }}; matematikk; bai

Her definerer vi et objekt som heter matte som har en enkelt funksjon kalt økning. Ved å bruke dette paradigmet kan vi til og med etterligne grunnleggende modularitet!

8. Konklusjon

Denne artikkelen utforsket noen av funksjonene i Nashorn JavaScript-motor. Eksempler som er vist her brukte bokstavelige skript, men for virkelige scenarier, vil du mest sannsynlig ha skriptet ditt i separate filer og laste dem ved hjelp av en Leser klasse.

Som alltid er koden i denne oppskriften tilgjengelig på GitHub.