Specify scope for eval() in JavaScript?

后端 未结 9 1155
心在旅途
心在旅途 2020-11-27 07:24

is there any way I can execute eval() on a specific scope (but NOT global)?

for example, the following code doesn\'t work (a is undefined on the sec

相关标签:
9条回答
  • 2020-11-27 07:43

    you can use the "use strict" to contain the eval'ed code within the eval itself.

    Second, eval of strict mode code does not introduce new variables into the surrounding scope. In normal code eval("var x;") introduces a variable x into the surrounding function or the global scope. This means that, in general, in a function containing a call to eval every name not referring to an argument or local variable must be mapped to a particular definition at runtime (because that eval might have introduced a new variable that would hide the outer variable). In strict mode eval creates variables only for the code being evaluated, so eval can't affect whether a name refers to an outer variable or some local variable

    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
    

    If a function is declared with "use strict", everything in it will be executed in strict mode. the following will do the same as above:

    function foo(){
        "use strict";
    
         var x = 17;
         var evalX = eval("var x = 42; x");
         assert(x === 17);
         assert(evalX === 42);
    }
    
    0 讨论(0)
  • 2020-11-27 07:44

    Simple as pie.

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

    0 讨论(0)
  • 2020-11-27 07:45

    This worked for me the best:

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

    Usage:

    scopedEval({a:1,b:2},"return this.a+this.b")
    
    0 讨论(0)
  • 2020-11-27 07:46

    You can look into the vm-browserify project, which would be used in conjunction with browserify.

    It works by creating <iframe>s, and evaling the code in that <iframe>. The code is actually pretty simple, so you could adapt the basic idea for your own purposes if you don't want to use the library itself.

    0 讨论(0)
  • 2020-11-27 07:47

    Here is a 20-line or so JS class that implements an extensible context using eval in a lexical scope:

    // 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>

    0 讨论(0)
  • 2020-11-27 07:49

    Create the variables you want to exist in your scope as local variables in a function. Then, from that function, return a locally-defined function that has a single argument and calls eval on it. That instance of eval will use the scope of its containing function, which is nested inside the scope of your top level function. Each invocation of the top level function creates a new scope with a new instance of the eval function. To keep everything dynamic, you can even use a call to eval in the top level function to declare the variables that will be local to that scope.

    Example code:

    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/

    0 讨论(0)
提交回复
热议问题