所以我们在 2020 年,我有这个要求,不幸的是,大多数答案根本不适合这项任务。
您可能可以跳过这部分,但要解释一下情况......这里有一个更完整的答案。
调用 eval 很糟糕...而在这里我正试图这样做...不是因为我想要,而是因为有人强迫我这样做...我的另一个选择是编译 extract 和 AST 并最终评估值的上下文...但是 eval 除了其自身的邪恶性质之外,它正是该任务的工具...
所以我们开始了,mdn 文档:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
简而言之,eval 曾经可以工作,但现在它变得更加难以使用,因为评估的上下文将成为全局上下文,因此在某些情况下不可能获得 this。并且由于某些原因,使用此问题的答案似乎无法将 this 作为评估上下文。
所以进一步阅读,我们到达了文章的第二部分..
文章的第二部分以“永远不要使用eval()!”开头,消息已收到!进一步阅读,我们得出以下结论:
幸运的是,eval() 有一个很好的替代方法:只需使用
window.Function().
好的!
所以看看接缝非常好的代码......大多数示例只是创建一个函数调用它并将其丢弃。这就是 eval 的工作原理,所以你也可以这样做......
但对我来说,eval 上下文可以重复使用,而且可能经常被重复使用,所以我想出了这个。
function create_context_function_template(eval_string, context) {
return `
return function (context) {
"use strict";
${Object.keys(context).length > 0
? `let ${Object.keys(context).map((key) => ` ${key} = context['${key}']`)};`
: ``
}
return ${eval_string};
}
`
}
这会编译一个函数,该函数接收要在给定特定上下文的情况下评估的上下文。这对于您知道评估上下文将始终包含一组特定键的情况很有用...
函数的第一行通过从作为参数传递的上下文中获取值来生成本地范围内声明变量的列表。
这将在给定上下文的情况下呈现如下所示的 JS 代码:{a: 1, b: 2}
let a = context['a'], b = context['b'];
第二行是你想要评估的上下文,所以对于像这样的'a + b'
它将呈现以下代码:
return a + b
总而言之,有这个实用方法:
function make_context_evaluator(eval_string, context) {
let template = create_context_function_template(eval_string, context)
let functor = Function(template)
return functor()
}
这只是简单地把它包装起来并返回我们需要的函子......
let context = {b: (a, b) => console.log(a, b)}
let evaluator = make_context_evaluator("b('foo', 'bar')", context)
let result = evaluator(context)
这样做的好处是,如果您想继续使用评估器,因为您知道您不会过多地更改评估上下文...然后您可以保存评估器并在不同的上下文中重用它..
在我的情况下,它是根据一些记录来评估上下文,因此字段集相同但值不同...因此可以重用该方法而无需编译多个方法...另一方面另外,据说它比使用eval 更快,而且我们没有评估任何东西。如果传递给Function 的值会尝试使用其范围之外的东西......它的危害会比eval 小......例如,它可以访问全局范围但不应该访问到词法范围。换句话说...您仅限于全局范围,并且 this 传递给调用参数。
如果你真的想将它用作类似 eval 的函数,你可以这样做:
function create_context_function_template(eval_string, context) {
return `
return function (context) {
"use strict";
${Object.keys(context).length > 0
? `let ${Object.keys(context).map((key) => ` ${key} = context['${key}']`)};`
: ``
}
return ${eval_string};
}
`
}
function make_context_evaluator(eval_string, context) {
let template = create_context_function_template(eval_string, context)
let functor = Function(template)
return functor()
}
function eval_like(text, context={}) {
let evaluator = make_context_evaluator(text, context)
return evaluator(context)
}
这里是一个如何使用的例子:
< eval_like("fun + 2", {fun: 1})
> 3
或者你甚至可以像这样传递一个对象。
> this.fun = 1
> eval_like("fun + 2", this)
> 3
您可以像函数声明一样传递表达式,但是...
> eval_like("() => fun", {fun: 2})()
它会起作用,但重要的是需要评估的所有内容都在上下文中。如果上下文值不存在,如果我没记错的话,Javascript 会尝试从全局对象中获取它们。
因此,为了正常工作,您必须确保代码中没有变量取自本地范围。