Укажите область для eval () в JavaScript? - PullRequest
21 голосов
/ 20 марта 2012

есть ли способ, как я могу выполнить 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
}

Есть идеи о том, как добиться чего-то подобного?Спасибо!

Ответы [ 4 ]

17 голосов
/ 20 марта 2012

вы можете использовать «использовать строгий» для хранения eval'ed-кода внутри самого 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);
}
7 голосов
/ 26 октября 2016

Создайте переменные, которые вы хотите иметь в своей области видимости, как локальные переменные в функции.Затем из этой функции верните локально определенную функцию с единственным аргументом и вызовом 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/

3 голосов
/ 20 марта 2012

Вы можете заглянуть в проект vm-browserify , который будет использоваться вместе с browserify .

. Он работает путем создания <iframe> s иeval код в этом <iframe>.Код на самом деле довольно прост, поэтому вы можете адаптировать основную идею для своих собственных целей, если не хотите использовать саму библиотеку.

2 голосов
/ 09 ноября 2016

Вот примерно 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>
...