2012-03-20 56 views
17

有没有什么办法可以在特定范围内执行eval()(但不是全局)在JavaScript中指定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

如果有人仍然感兴趣,我只是在这里发布一个答案http://stackoverflow.com/a/43306962/1758245 – 2017-04-09 13:06:11

回答

13

您可以使用"use strict"在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 

如果一个函数与“使用申报严格“,所有内容都将严格执行。下面将做与上面相同:

function foo(){ 
    "use strict"; 

    var x = 17; 
    var evalX = eval("var x = 42; x"); 
    assert(x === 17); 
    assert(evalX === 42); 
} 
+1

作为一个方面说明,'严格使用'**与**将导致异常。 Quote:With(){}语句在启用严格模式时死了 - 事实上,它甚至显示为语法错误。来源:http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ – 2012-03-20 11:43:00

+0

它不适合我。我用Chrome试过它 – Zo72 2012-11-21 21:45:28

+0

@Joseph梦想家这对我不起作用。我用Chrome试了一下。哪个浏览器应该支持这个? – Zo72 2012-11-21 21:51:28

1

你可以窥视vm-browserify项目,该项目将结合browserify使用。

它通过在中创建 s和eval代码来工作。代码实际上非常简单,所以如果您不想使用库本身,您可以根据自己的目的调整基本思路。

4

在您的作用域中创建想要存在的变量作为函数中的局部变量。然后,从该函数返回一个本地定义的函数,该函数只有一个参数,并且在其上调用evaleval的实例将使用它的包含函数的作用域,它嵌套在顶级函数的作用域内。顶层函数的每次调用都会创建一个新的作用域,并带有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);"); 

https://jsfiddle.net/zgs73ret/

1

这里是一个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) { 
 
\t 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>