Search code examples
javanashornjsobject

Questions about Nashorn - JSObject


I want to create a JSObject and fill it with properties (kind of like a HashMap), but without casting the result of an eval("({})"), because I would think that constantly evaluating such a thing would really have an impact on performance. Is there a way?


Solution

  • If you want to use script objects like Maps, you might as well use java.util.HashMap instances from Nashorn scripts! In addition to supporting the usual java method calls, Nashorn's linker special cases java.util.Map instances and supports keys-as-property names idiom. This is more efficient than using a Javascript object as a Map.

    $ jjs
    jjs> var m = new java.util.HashMap
    jjs> m.foo = "bar"
    bar
    jjs> m.foo
    bar
    jjs> m.get("foo")
    bar
    jjs> m.put("js", "nashorn")
    null
    jjs> m.js
    nashorn
    

    But, if you insist on using JS-object-as-map, then you can do the eval and cast to JSObject you mentioned. You may want to measure the perf. hit (which is assumed!) before making any further changes!

    You can also get hold of JS "Object" constructor object and 'cache' it for repeated object creation from Java code. You can then call newObject() method on it to create a new empty object.

    import javax.script.*;
    import jdk.nashorn.api.scripting.JSObject;
    
    public class Main {
       public static void main(String[] args) throws Exception {
          ScriptEngineManager m = new ScriptEngineManager();
          ScriptEngine e = m.getEngineByName("nashorn");
    
          // get JS "Object" constructor object
          JSObject objConstructor = (JSObject)e.eval("Object");
    
          // call 'newObject' on Object constructor
          JSObject jsObj = (JSObject) objConstructor.newObject();
    
          // fill properties of the new empty script object
          jsObj.setMember("foo", "bar");
    
          // expose the new JS object as global var!
          e.put("obj", jsObj);
    
          // print the object as a JSON string
          e.eval("print(JSON.stringify(obj))");
       }
    }
    

    Note that the above scheme works for any user-defined constructor function as well. For eg. if you want to create objects using specific user defined constructor function, you just have to replace

      JSObject objConstructor = (JSObject)e.eval("Object");
    

    with

      JSObject objConstructor = (JSObject)e.eval("MyConstructorFunc");
    

    (assuming you've eval'ed code to define MyConstructorFunc function earlier). The rest of the code is same as above.