在 JavaScript 中指定 eval() 的范围?

IT技术 javascript scope eval
2021-02-27 20:06:15

有什么方法可以在特定范围(但不是全局)上执行 eval( )吗?

例如,以下代码不起作用(a 在第二个语句中未定义),因为它们在不同的范围内:

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
}

关于如何实现这样的目标的任何想法?谢谢!

6个回答

您可以使用“使用严格”在 eval 本身中包含 eval'ed 代码。

其次,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);
}
作为旁注,使用 'use strict' with将导致异常。引用: with(){} 语句在启用严格模式时已失效 - 事实上它甚至显示为语法错误。来源:ejohn.org/blog/ecmascript-5-strict-mode-json-and-more
2021-04-17 20:06:15
@Joseph 梦想家这对我不起作用。我用 Chrome 试过了。哪个浏览器应该支持这个?
2021-05-08 20:06:15
这段代码似乎没有做 OP 所要求的。这似乎可以防止调用eval更改它们被调用的范围的内容,但是提问者希望允许调用者eval控制在哪个范围内eval操作,而不是阻止修改(他的工作方式示例包含此类修改,因此显然这是他想要的)
2021-05-09 20:06:15

创建您希望存在于作用域中的变量作为函数中的局部变量。然后,从该函数返回一个本地定义的函数,该函数具有单个参数并对其进行调用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);");

https://jsfiddle.net/zgs73ret/

简单如馅饼。

// Courtesy of Hypersoft-Systems: U.-S.-A.
function scopeEval(scope, script) {
  return Function('"use strict";return (' + script + ')').bind(scope)();
}

scopeEval(document, 'alert(this)');

Function.bind(scope) 解决了我的问题。非常感谢!
2021-04-20 20:06:15
bind方法绑定函数 ( this)的接收者而不是作用域。这意味着您必须将所有变量称为this.varname.
2021-05-07 20:06:15
下面的代码不会被作用域 scopeEval('Evaluated inside scope', "new Function('alert(this)')()")
2021-05-15 20:06:15

这是一个 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>

要将变量“添加到范围”,您可以使用旧范围内的新变量创建一个新范围。新作用域将继承旧作用域的变量。不过,这不会更改旧范围的变量集。您可以使用新范围的 eval 的值分配旧范围的 eval 属性。
2021-04-22 20:06:15
很好的补充和很好的例子,谢谢!我想知道是否有办法将值“注入”到作用域中。例如scope.define("x", 3)
2021-05-02 20:06:15

这对我最有效:

const scopedEval = (scope, script) => Function(`"use strict"; ${script}`).bind(scope)();

用法:

scopedEval({a:1,b:2},"return this.a+this.b")
优秀的!这个惊人的小代码片段是我发现执行注入到 ShadowRoot 中的脚本的唯一方法,该脚本实际上可以在该范围内进行交互。
2021-05-06 20:06:15