我已经(在 JavaScript 中)编写了一个交互式 read-eval-print-loop,它被封装
一个对象内。但是,我最近注意到解释器指定的顶级函数定义似乎没有被解释器“记住”。经过一些诊断工作,我将核心问题简化为:
var evaler = {
eval: function (str)
{
return eval(str);
},
};
eval("function t1() { return 1; }"); // GOOD
evaler.eval("function t2() { return 2; }"); // FAIL
在这一点上,我希望以下两个语句能按预期工作:
print(t1()); // => Results in 1 (This works)
print(t2()); // => Results in 2 (this fails with an error that t2 is undefined.)
我得到的是
t1
的预期值行和 t2
行失败,错误为 t2
是未绑定(bind)的。IOW:运行此脚本后,我有一个
t1
的定义。 , 并且没有 t2
的定义.从 evaler
内部调用 eval 的行为与顶级调用有很大不同,因此全局定义不会被记录。发生的事情是调用evaler.eval
返回一个函数对象,所以我假设 t2
正在被定义并存储在我无权访问的其他一些绑定(bind)中。 (它没有被定义为 evaler
中的成员。)有什么简单的解决方法吗?我已经尝试了各种修复方法,并没有偶然发现一种有效的方法。 (我所做的大部分工作都集中在将对 eval 的调用放在匿名函数中,并改变调用方式,链接
__parent__
等)关于如何解决这个问题的任何想法?
这是进一步调查的结果:
tl;dr:Rhino 在调用实例上的方法时会在作用域链中添加一个中间作用域。
t2
被定义在这个中间范围内,立即被丢弃。 @Matt:您的“hacky”方法很可能是解决此问题的最佳方法。我仍在为根本原因做一些工作,但是由于在 jdb 上度过了一段美好的时光,我现在对正在发生的事情有了更多的了解。如前所述,函数语句如
function t1() { return 42; }
做两件事。function() { return 42; }
获得的一样。 t1
的当前顶级作用域。 . 我最初的问题是为什么我在调用
eval
时没有看到第二件事发生。从对象的方法中。在 Rhino 中实际执行绑定(bind)的代码似乎在函数
org.mozilla.javascript.ScriptRuntime.initFunction
中。 . if (type == FunctionNode.FUNCTION_STATEMENT) {
....
scope.put(name, scope, function);
对于
t1
上例,scope
是我设置的顶级范围。这是我想要定义我的顶级函数的地方,所以这是一个预期的结果:main[1] print function.getFunctionName()
function.getFunctionName() = "t1"
main[1] print scope
scope = "com.me.testprogram.Main@24148662"
然而,在
t2
案例,scope
完全是另一回事:main[1] print function.getFunctionName()
function.getFunctionName() = "t2"
main[1] print scope
scope = "org.mozilla.javascript.NativeCall@23abcc03"
它是
NativeCall
的父范围这是我预期的顶级范围:main[1] print scope.getParentScope()
scope.getParentScope() = "com.me.testprogram.Main@24148662"
这或多或少是我在上面写这个时所害怕的:“在直接 eval 的情况下,t2 被绑定(bind)在全局环境中。在 evaler 的情况下,它被绑定(bind)在 'elsewhere'” 在这种情况下,'elsewhere ' 结果是
NativeCall
的实例... t2
函数被创建,绑定(bind)到 t2
NativeCall
中的变量,以及 NativeCall
当调用 evaler.eval
时消失返回。这就是事情变得有点模糊的地方......我没有做我想做的那么多分析,但我目前的工作理论是
NativeCall
需要范围以确保 this
指向 evaler
在调用 evaler.eval
时执行. (稍微备份堆栈帧,NativeCall
被 Interpreter.initFrame
添加到作用域链中,当函数“需要激活”并且具有非零函数类型时。我假设这些事情对于简单来说都是正确的仅函数调用,但尚未对上游进行足够的跟踪以确切知道。也许明天。)
最佳答案
您的代码实际上根本没有失败。 eval
正在返回 function
你永远不会调用它。
print(evaler.eval("function t2() { return 2; }")()); // prints 2
再详细说明一下:
x = evaler.eval("function t2() { return 2; }"); // this returns a function
y = x(); // this invokes it, and saves the return value
print(y); // this prints the result
编辑
回应:
Is there another way to create an interactive read-eval-print-loop than to use eval?
由于您使用的是 Rhino.. 我想您可以使用 java Process 对象调用 Rhino 来使用 js 读取文件?
假设我有这个文件:
test.js
function tf2() {
return 2;
}
print(tf2());
然后我可以运行这段代码,它会调用 Rhino 来评估该文件:
process = java.lang.Runtime.getRuntime().exec('java -jar js.jar test.js');
result = java.io.BufferedReader(java.io.InputStreamReader(process.getInputStream()));
print(result.readLine()); // prints 2, believe it or not
所以你可以更进一步,通过编写一些代码来评估文件,然后调用上面的代码......
是的,这很荒谬。
关于javascript - 从对象内调用时使用 'eval' 定义顶级函数时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2999207/