有什么办法可以在特定范围内执行eval()(但不是全局)?
例如,以下代码不起作用(在第二个语句中未定义)因为它们位于不同的范围内:
eval(var a = 1);
eval(alert(a));
如果可能的话,我想动态创建一个范围。例如(语法肯定是错误的,但只是为了说明这个想法)
var scope1;
var scope2;
with scope1{
eval(var a = 1); eval(alert(a)); // this will alert 1
}
with scope2{
eval(var a = 1); eval(a++); eval(alert(a)); // this will alert 2
}
with scope1{
eval(a += 2); eval(alert(a)); // this will alert 3 because a is already defined in scope1
}
关于如何实现这样的事情的任何想法?谢谢!
答案 0 :(得分:16)
您可以使用"use strict"在eval中包含eval代码。
其次,
eval
严格模式代码不会将新变量引入周围范围。在普通代码eval("var x;")
中,将变量x
引入周围函数或全局范围。这意味着,通常,在包含对eval
的调用的函数中,每个不引用参数或局部变量的名称必须在运行时映射到特定定义(因为eval
可能引入了隐藏外部变量的新变量。 在严格模式下eval
仅为正在评估的代码创建变量,因此eval不会影响名称是指外部变量还是某个局部变量
var x = 17; //a local variable
var evalX = eval("'use strict'; var x = 42; x"); //eval an x internally
assert(x === 17); //x is still 17 here
assert(evalX === 42); //evalX takes 42 from eval'ed x
如果使用“use strict”声明函数,则其中的所有内容都将以严格模式执行。以下内容与上述相同:
function foo(){
"use strict";
var x = 17;
var evalX = eval("var x = 42; x");
assert(x === 17);
assert(evalX === 42);
}
答案 1 :(得分:7)
在作用域中创建要作为局部变量存在的变量。然后,从该函数返回一个本地定义的函数,该函数具有单个参数并在其上调用eval
。 eval
的该实例将使用其包含函数的范围,该函数嵌套在顶级函数的范围内。顶级函数的每次调用都会创建一个带有eval函数新实例的新范围。为了保持动态,您甚至可以在顶级函数中调用eval
来声明该范围的本地变量。
示例代码:
function makeEvalContext (declarations)
{
eval(declarations);
return function (str) { eval(str); }
}
eval1 = makeEvalContext ("var x;");
eval2 = makeEvalContext ("var x;");
eval1("x = 'first context';");
eval2("x = 'second context';");
eval1("window.alert(x);");
eval2("window.alert(x);");
答案 2 :(得分:3)
您可以查看vm-browserify项目,该项目将与browserify一起使用。
通过在<iframe>
中创建eval
和<iframe>
代码来工作。代码实际上非常简单,因此如果您不想使用库本身,可以根据自己的需要调整基本思想。
答案 3 :(得分:3)
很简单。
// Courtesy of Hypersoft-Systems: U.-S.-A.
function scopeEval(scope, script) {
return Function('"use strict";return (' + script + ')').bind(scope)();
}
scopeEval(document, 'alert(this)');
答案 4 :(得分:2)
这是一个20行左右的JS类,它在词法范围内使用eval实现可扩展的上下文:
// Scope class
// aScope.eval(str) -- eval a string within the scope
// aScope.newNames(name...) - adds vars to the scope
function Scope() {
"use strict";
this.names = [];
this.eval = function(s) {
return eval(s);
};
}
Scope.prototype.newNames = function() {
"use strict";
var names = [].slice.call(arguments);
var newNames = names.filter((x)=> !this.names.includes(x));
if (newNames.length) {
var i, len;
var totalNames = newNames.concat(this.names);
var code = "(function() {\n";
for (i = 0, len = newNames.length; i < len; i++) {
code += 'var ' + newNames[i] + ' = null;\n';
}
code += 'return function(str) {return eval(str)};\n})()';
this.eval = this.eval(code);
this.names = totalNames;
}
}
// LOGGING FOR EXAMPLE RUN
function log(s, eval, expr) {
s = '<span class="remark">' + String(s);
if (expr) {
s += ':\n<b>' + expr + '</b> --> ';
}
s += '</span>';
if (expr) {
try {
s += '<span class="result">' + JSON.stringify(eval(expr)) + '</span>';
} catch (err) {
s += '<span class="error">' + err.message + '</span>';
}
}
document.body.innerHTML += s + '\n\n';
}
document.body.innerHTML = '';
// EXAMPLE RUN
var scope = new Scope();
log("Evaluating a var statement doesn't change the scope but newNames does (should return undefined)", scope.eval, 'var x = 4')
log("X in the scope object should raise 'x not defined' error", scope.eval, 'x');
log("X in the global scope should raise 'x not defined' error", eval, 'x');
log("Adding X and Y to the scope object");
scope.newNames('x', 'y');
log("Assigning x and y", scope.eval, 'x = 3; y = 4');
log("X in the global scope should still raise 'x not defined' error", eval, 'x');
log("X + Y in the scope object should be 7", scope.eval, 'x + y');
log("X + Y in the global scope should raise 'x not defined' error", eval, 'x + y');
.remark {
font-style: italic;
}
.result, .error {
font-weight: bold;
}
.error {
color: red;
}
<body style='white-space: pre'></body>
答案 5 :(得分:1)
这对我来说是最好的:
const scopedEval = (scope, script) => Function(`"use strict"; ${script}`).bind(scope)();
用法:
scopedEval({a:1,b:2},"return this.a+this.b")
答案 6 :(得分:0)
穷人的方法:
如果范围不是太动态,只需几个静态和只读声明,只需将其放在字符串中并与要执行的字符串连接即可,如下所示:
const scopeAll = `
const myFunc = (a, b) => a + b + s;
`
const scope1 = `
${scopeAll}
const s = 'c';
`
const scope2 = `
${scopeAll}
const s = 'd';
`
const myStringToExecute = `
myFunc('a', 'b')
`
console.log(eval(scope1 + myStringToExecute));
console.log(eval(scope2 + myStringToExecute));
答案 7 :(得分:0)
这里的方法是允许上下文对象参数化表达式的求值。
首先,使用Function() constructor创建一个函数,该函数接受上下文的每个键以及要求值的表达式;主体返回评估的表达式。然后,使用上下文的所有值和要求值的表达式调用该函数。
function scopedEval(context, expr) {
const evaluator = Function.apply(null, [...Object.keys(context), 'expr', "return eval(expr)"]);
return evaluator.apply(null, [...Object.values(context), expr]);
}
// Usage
const context = {a: 1, b: 2, c: {d: 3}};
scopedEval(context, "a+b+c.d"); // 6
通过使用Function.prototype.apply,无需事先知道参数的数量和名称。因为参数的作用域为evaluator
,所以可以直接从表达式访问它们(而不是要求this.a
)。
答案 8 :(得分:0)
这是我发现的最简单的方法,但它不使用eval。
function execInContext(code, context)
{
return Function(...Object.keys(context), 'return '+ code (...Object.values(context));
}
在这里,我们创建一个Function
对象。 Function
构造函数接受一个参数数组,最后一个是函数要执行的代码,其他所有参数都是函数的参数名。我们正在做的是创建一个函数,该函数的参数名称与context
对象中的字段相同,然后使用context
中的字段值调用此函数。因此,如果您致电
execInContext('myVar', {myVar: 'hi!'});
与
相同((myVar) => { return myVar; })('hi!');
,结果将为hi!