Javascript 上下文保持评估
我们正在构建一个小的REPL,它在用户输入javascript表达式时(使用Javascript 上下文保持评估,javascript,scope,eval,Javascript,Scope,Eval,我们正在构建一个小的REPL,它在用户输入javascript表达式时(使用eval)对其进行计算。由于整个过程都是事件驱动的,因此计算必须在单独的函数中进行,但上下文(即所有声明的变量和函数)必须在调用之间保留。我提出了以下解决方案: 函数*{ 而(1){ 试一试{ s=产量评估(s) }捕捉(错误){ s=收益率误差 } } } 让_eval=_eval() _下一次评估() 功能评估(expr){ 让结果=_eval.next(expr.value) 如果(错误的结果实例) consol
eval
)对其进行计算。由于整个过程都是事件驱动的,因此计算必须在单独的函数中进行,但上下文(即所有声明的变量和函数)必须在调用之间保留。我提出了以下解决方案:
函数*{
而(1){
试一试{
s=产量评估(s)
}捕捉(错误){
s=收益率误差
}
}
}
让_eval=_eval()
_下一次评估()
功能评估(expr){
让结果=_eval.next(expr.value)
如果(错误的结果实例)
console.log(expr,'ERROR:',result.message)
其他的
console.log(expr,'=>',result)
}
评估('var十=10')
求值('函数立方体(x){返回x**3}')
评估(‘十+立方(3)’)
评估('console.log(“副作用”))
评估(‘设二十=20’)
evaluate('two+40')//问题
如果用户输入的代码在使用evaluate
之外没有任何副作用,一种方法是将新输入字符串连接到旧输入字符串上。例如:
evaluate('ten + cube(3)')
evaluate('let twenty = 20')
运行以下命令的结果。第一次:
ten + cube(3)
第二次:
ten + cube(3)
let twenty = 20
这不是很优雅,因为每次代码都必须运行之前输入的所有代码,但它至少会使repl正常工作
function*\u EVAL(代码汇总){
让userCode='';
而(1){
而(!密码术){
codeToTry=产量为零;
}
试一试{
const newCode=userCode+';'+codetory;
const result=eval(新代码)
//没有错误,因此附加用户代码:
userCode=newCode;
codeToTry=产量结果;
}捕捉(错误){
//错误,不附加到用户代码:
codeToTry=收益率误差
}
}
}
让_eval=_eval()
_下一次评估()
功能评估(expr){
让结果=_eval.next(expr.value)
如果(错误的结果实例)
console.log(expr,'ERROR:',result.message)
其他的
console.log(expr,'=>',result)
}
评估('var十=10')
求值('函数立方体(x){返回x**3}')
评估(‘十+立方(3)’)
评估(‘设二十=20’)
evaluate('twenty+40')//问题
如果运行5,则返回5
是本机JavaScript行为;设a=2,当您检查two+40
的值时,程序的最终语句中基本上就是这样。但是,一个快速的解决方法是收集两个结果,完整结果fullResult
,以及该步骤的结果(stepResult
)。使用这两种方法,一旦evaluate()
函数成功,我们可以检查stepResult
是否等于undefined
,这在分配新变量值时发生
如果是这种情况,我们将使用该值undefined
。否则,我们使用fullResult
的值,该值适用于您问题中提供的代码的每种情况:
const pastEvals=[];
函数*_EVAL(s){
而(1){
试一试{
s=产量评估(s)
}捕捉(错误){
s=收益率误差
}
}
}
让_eval=_eval()
_下一次评估()
功能评估(expr){
粘贴推送(expr)
const fullResult=_eval.next(pastEvals.join(“;”).value
const stepResult=_eval.next(expr.value)
if(fullResult instanceof Error)
console.log(expr,'ERROR:',result.message)
其他的
log(expr,'==>',stepResult===未定义?stepResult:fullResult);
}
评估('var十=10')
求值('函数立方体(x){返回x**3}')
评估(‘十+立方(3)’)
评估(‘设二十=20’)
evaluate('twenty+40')
包含一种实际有效的疯狂方法:在每次eval()
调用期间,我们在eval
范围内创建一个新的闭包,并将其导出,以便我们可以使用它来评估下一个语句
var\uu EVAL=s=>EVAL(`void(\uu EVAL=${\uu EVAL.toString()});${s});
功能评估(expr){
试一试{
const result=uu EVAL(expr);
console.log(expr,'=>',result)
}捕捉(错误){
console.log(expr,'ERROR:',err.message)
}
}
评估('var十=10')
求值('函数立方体(x){返回x**3}')
评估(‘十+立方(3)’)
评估('console.log(“副作用”))
评估(‘设二十=20’)
评估('二十+40')//没问题:D
TL;博士
下面是我推荐的最佳解决方案,它支持所有表达式,包括基于承诺的表达式,如fetch()
,使用async
/wait
和嵌套evaluate()
在我的fetch()
的最终then()
中
注(以下全文中也提到)
首先记录嵌套的evaluate()
表达式的结果。这是正确的,也是预期的,因为嵌套表达式在运行它的fetch()
中运行。一旦整个fetch运行,它将返回undefined
,就像变量赋值一样。对于我下面的回答中的每一个其他[不推荐的]解决方案,如果fetch()
语句已成功完成评估,则将评估title
变量。这是因为我们要么通过setTimeout()
或预处理的then()
强制推迟title
变量的评估,要么通过强制顺序加载此解决方案底部的“奖金”解决方案。
如果您在前端环境中,另一种方法是每次附加一个
标记。但是,您需要某种方式将给定输入字符串中的最终语句的结果以某种方式返回给用户,这可能需要像Acorn这样的成熟解析器。瓦古