我有一个Java
7程序,该程序加载成千上万个对象(组件),每个对象具有许多参数(存储在中Map
),并在这些对象上执行各种Rhino脚本以计算其他派生参数,这些参数又存储回该对象的中Map
。在运行每个脚本之前,将Scope
创建一个对象,该对象由该对象的映射支持,该映射在脚本运行期间用作JavaScript的作用域。
作为一个简单的示例,以下代码创建一个HashMap
a = 10和b = 20的代码,并执行脚本c = a + b
,结果c = 30.0
将其存储回地图中。尽管脚本看起来像是在创建全局变量c
,但是Scope
对象会捕获它并将其存储在地图中;使用不同Scope
对象执行的另一个脚本将看不到此变量:
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";
}
}
}
上面的脚本输出{b=20, c=30.0, a=10}
显示变量c
已存储在中Map
。
现在,我需要将其迁移到Java
8并使用Nashorn。但是,我发现Nashorn总是将全局变量存储在一个特殊"nashorn.global"
对象中。实际上,它似乎将所有绑定视为只读,并且尝试更改现有变量会导致新的全局变量遮盖了现有绑定。
public class Nashorn {
private final static ScriptEngineManager MANAGER = new ScriptEngineManager();
public static void main(String[] args) throws ScriptException {
new Nashorn().testBindingsAsArgument();
new Nashorn().testScopeBindings("ENGINE_SCOPE", ScriptContext.ENGINE_SCOPE);
new Nashorn().testScopeBindings("GLOBAL_SCOPE", ScriptContext.GLOBAL_SCOPE);
}
private ScriptEngine engine = MANAGER.getEngineByName("nashorn");
private Map<String, Object> map = new HashMap<>();
private Bindings bindings = new SimpleBindings(map);
private Nashorn() {
map.put("a", 10);
map.put("b", 20);
}
private void testBindingsAsArgument() throws ScriptException {
System.out.println("Bindings as argument:");
engine.eval("c = a + b; a += b", bindings);
System.out.println("map = " + map);
System.out.println("eval('c', bindings) = " + engine.eval("c", bindings));
System.out.println("eval('a', bindings) = " + engine.eval("a", bindings));
}
private void testScopeBindings(String scope_name, int scope) throws ScriptException {
System.out.println("\n" + scope_name + ":");
engine.getContext().setBindings(bindings, scope);
engine.eval("c = a + b; a += b");
System.out.println("map = " + map);
System.out.println("eval('c') = " + engine.eval("c"));
System.out.println("eval('a') = " + engine.eval("a"));
}
}
输出:
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()
with map.put("c", engine.get("c"))
将把结果移动到我需要的位置,但是使用任意脚本,我不知道所有变量名都是什么,因此不是一种选择。
那么问题来了:是否有捕获全局变量的创建并将其存储在应用程序控制下的Java对象中的方法,例如原始Binding对象?
我有一个可行的解决方案,但这显然是一个hack。
测试程序:
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 = {a=30.0, b=20, c=30.0}
根据需要输出。
GlobalMap
截取项将拦截Nashorn全局对象在key下的"nashorn.global"
存储,因此不会将其存储在地图中。当GlobalMap
关闭时,它会从原地图的犀牛全局对象,并将它们存储任何新的全局变量:
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));
}
}
}
我仍然希望找到一种解决方案,将当前范围设置为Map<String,Object>
或Bindings
对象,并将脚本创建的任何新变量直接存储在该对象中。
如果你要多于一个函数共用一个简单的变量,简单的处理方法就是把这个变量在所有函数中定义为global全局变量。在命令行做同样的事情,如果你要工作空间访问上述变量。这个全局变量的定义必须出现在变量被应用于一个函数之前。虽然不是要求,但全局变量也最好以大写字母开头,这样可以同其他变量区别出来。举个例子,做一个以falling.m命名的M-文件。 function h = falling(t) globa
ThinkCMF封装了前台模板开发时常用的一些变量,这些变量是全局的,你在前台模板任何时候都能直接调用: {$site_name} /站点名称 {$site_host} /站点域名 {$site_root} /安装目录 {$site_icp} /
swoole内置了几个全局变量供程序内使用。一般不要自行创建全局变量。 SwooleG 超全局本地内存变量,此变量在swoole_init时就初始化好了。存储了一些全局的信息。但不是共享内存的。当创建子进程后再修改其中的字段,其他进程是感知不到的 SwooleG.main_reactor,全局事件循环 SwooleG.lock,全局锁 SwooleG.memory_pool,全局共享内存池 Swo
全局变量 PHP中在函数、类之外直接定义的变量可以在函数、类成员方法中通过global关键词引入使用,这些变量称为:全局变量。 这些直接在PHP中定义的变量(包括include、require文件中的)相对于函数、类方法而言它们是全局变量,但是对自身执行域zend_execute_data而言它们是普通的局部变量,自身执行时它们与普通变量的读写方式完全相同。 function test() {
本文向大家介绍Lua中的全局变量、非全局变量总结,包括了Lua中的全局变量、非全局变量总结的使用技巧和注意事项,需要的朋友参考一下 前言 Lua将其所有的全局变量保存在一个常规的table中,这个table称为“环境”。这种组织结构的优点在于,其一,不需要再为全局变量创造一种新的数据结构,因此简化了Lua的内部实现;另一个优点是,可以像其他table一样操作这个table。为了便于实施这种操作,L
主要内容:局部变量,全局变量,局部变量和全局变量的综合示例在《 C语言形参和实参的区别》中提到,形参变量要等到函数被调用时才分配内存,调用结束后立即释放内存。这说明形参变量的作用域非常有限,只能在函数内部使用,离开该函数就无效了。 所谓 作用域( Scope ) ,就是变量的有效范围。 不仅对于形参变量,C语言中所有的变量都有自己的作用域。决定变量作用域的是变量的定义位置。 局部变量 定义在函数内部的变量称为 局部变量(Local Variable) ,