Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/355.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 捕获纳肖恩';s全局变量_Javascript_Java_Rhino_Nashorn - Fatal编程技术网

Javascript 捕获纳肖恩';s全局变量

Javascript 捕获纳肖恩';s全局变量,javascript,java,rhino,nashorn,Javascript,Java,Rhino,Nashorn,我有一个Java 7程序,它加载数千个对象(组件),每个对象都有许多参数(存储在映射中),并在这些对象上执行各种Rhino脚本,以计算其他派生参数,这些参数存储在对象的映射中。在运行每个脚本之前,将创建一个范围对象,该对象由对象的映射支持,该映射在脚本期间用作JavaScript的范围 作为一个简单的示例,下面创建一个a=10和b=20的HashMap,并执行脚本c=a+b,这将导致c=30.0存储回映射中。尽管脚本看起来像是在创建一个全局变量c,但范围对象会捕获它并将其存储在地图中;使用不同的

我有一个Java 7程序,它加载数千个对象(组件),每个对象都有许多参数(存储在
映射
中),并在这些对象上执行各种Rhino脚本,以计算其他派生参数,这些参数存储在对象的
映射
中。在运行每个脚本之前,将创建一个
范围
对象,该对象由对象的映射支持,该映射在脚本期间用作JavaScript的范围

作为一个简单的示例,下面创建一个a=10和b=20的
HashMap
,并执行脚本
c=a+b
,这将导致
c=30.0
存储回映射中。尽管脚本看起来像是在创建一个全局变量
c
,但
范围
对象会捕获它并将其存储在地图中;使用不同的
范围执行的另一个脚本将看不到此变量:

public class Rhino {

    public static void main(String[] args) throws ScriptException {
        Context cx = Context.enter();
        Scriptable root_scope = cx.initStandardObjects();

        Map<String, Object> map = new HashMap<>();
        map.put("a", 10);
        map.put("b", 20);

        Scope scope = new Scope(root_scope, map);
        cx.evaluateString(scope, "c = a + b", "<expr>", 0, null);
        System.out.println(map); // --> {b=20, c=30.0, a=10}

        Context.exit();
    }

    static class Scope extends ScriptableObject {

        private Map<String, Object> map;

        public Scope(Scriptable parent, Map<String, Object> map) {
            setParentScope(parent);
            this.map = map;
        }

        @Override
        public boolean has(String key, Scriptable start) {
            return true;
        }

        @Override
        public Object get(String key, Scriptable start) {
            if (map.containsKey(key))
                return map.get(key);
            return Scriptable.NOT_FOUND;
        }

        @Override
        public void put(String key, Scriptable start, Object value) {
            map.put(key, value);
        }

        @Override
        public String getClassName() {
            return "MapScope";
        }
    }
}
输出:

Bindings as argument:
map = {a=10, b=20, nashorn.global=[object global]}
eval('c', bindings) = 30.0
eval('a', bindings) = 30.0

ENGINE_SCOPE:
map = {a=10, b=20, nashorn.global=[object global]}
eval('c') = 30.0
eval('a') = 30.0

GLOBAL_SCOPE:
map = {a=10, b=20}
eval('c') = 30.0
eval('a') = 30.0
eval
输出行显示结果已正确计算并已存储,但
map
输出行显示结果未存储在我希望的位置

由于各种原因,这是不可接受的。单个对象不会将计算出的参数存储回它们自己的本地存储器中。在其他对象上执行的其他脚本的变量将继承以前的脚本执行,这可能会隐藏逻辑错误(脚本可能会意外地使用未定义的变量名,但如果该名称实际上由以前的脚本使用,则可以使用旧的垃圾值,而不是生成
ReferenceError
,从而隐藏错误)

遵循
engine.eval()
map.put(“c”,engine.get(“c”))
会将结果移动到我需要的位置,但是使用任意脚本,我不知道所有变量名都是什么,所以这不是一个选项


所以问题是:是否有方法捕获全局变量的创建,并将它们存储在应用程序控制下的Java对象中,例如原始绑定对象???

我有一个解决方案,似乎可行,但显然是一个黑客

测试程序:

public class Nashorn {
    public static void main(String[] args) throws ScriptException {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");

        Map<String, Object> map = new HashMap<>();
        map.put("a", 10);
        map.put("b", 20);

        try (GlobalMap globals = new GlobalMap(map)) {
            engine.eval("c = a + b; a += b;", globals);
        }

        System.out.println("map = " + map);
    }
}

我仍然希望找到一种解决方案,将当前作用域设置为
Map
Bindings
对象,并且脚本创建的任何新变量都直接存储在该对象中。

我们不得不求助于此,这太愚蠢了,我打赌nashorn引擎与其创建者一样顽固……在iLife也能做到这一点(您需要使用
var
关键字来声明变量)。有人知道该方法是否有任何警告吗?@faizan感谢您的建议。使用带有
var c=…
的IIFE可以防止
c
存储在全局范围内。这有助于隔离代码,防止不同的脚本意外看到在其他脚本中计算的值。但这对orig没有帮助最后一个问题:在执行这些JavaScript代码片段的Java程序提供的
映射中,实际捕获存储到这些变量的计算值,以便稍后在程序的其他部分中使用。
public class Nashorn {
    public static void main(String[] args) throws ScriptException {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");

        Map<String, Object> map = new HashMap<>();
        map.put("a", 10);
        map.put("b", 20);

        try (GlobalMap globals = new GlobalMap(map)) {
            engine.eval("c = a + b; a += b;", globals);
        }

        System.out.println("map = " + map);
    }
}
public class GlobalMap extends SimpleBindings implements Closeable {

    private final static String NASHORN_GLOBAL = "nashorn.global";
    private Bindings global;
    private Set<String> original_keys;

    public GlobalMap(Map<String, Object> map) {
        super(map);
    }

    @Override
    public Object put(String key, Object value) {
        if (key.equals(NASHORN_GLOBAL) && value instanceof Bindings) {
            global = (Bindings) value;
            original_keys = new HashSet<>(global.keySet());
            return null;
        }
        return super.put(key, value);
    }

    @Override
    public Object get(Object key) {
        return key.equals(NASHORN_GLOBAL) ? global : super.get(key);
    }

    @Override
    public void close() {
        if (global != null) {
            Set<String> keys = new HashSet<>(global.keySet());
            keys.removeAll(original_keys);
            for (String key : keys)
                put(key, global.remove(key));
        }
    }
}