在有限的上下文中运行JS代码

Dan*_*dan 5 javascript

我正试图在"孤立"的上下文中运行受信任的 JS代码.基本上想出了这个方法:

function limitedEval(src, context) {
    return (function() {
        with(this) {
            return eval(src) 
        }
    }).call(context)
}
Run Code Online (Sandbox Code Playgroud)

这很好用,但是当脚本使用var关键字时,它存储在执行上下文中,而不是with语句中提供的上下文(我理解这是设计).例如,以下代码不起作用:

var ctx = {};
limitedEval('var foo = "hello"', ctx);
limitedEval('alert(foo)', ctx); // error: foo is undefined
Run Code Online (Sandbox Code Playgroud)

我希望能够多次调用limitedEval()并重用上下文.那可能吗?

gui*_*ino 5

这似乎是一个非常有趣的问题.代码的问题在于每次执行时都会生成新函数limitedEval.这意味着您使用var关键字创建的任何变量都只存在于该函数的上下文中.你真正需要的是每个上下文有1个函数并重用该函数的上下文.最明显的方法是使用生成器.生成器本质上是可以暂停然后重新启动的函数.

// IIFE to store gen_name
var limitedEval = function() {
    // Symbol for generator property so we don't pollute `ctx` namespace
    var gen_name = Symbol();

    return function limitedEval(src, context) {
        if(!(gen_name in context)) {
            // Generator that will run eval and pause til getting the next source code
            var generator = function * () {
                with(this) {
                    while(true) {
                        yield eval( yield );
                    }
                }
            };

            context[gen_name] = generator.call(context);

            // Initially, we need to execute code until reaching the first `yield`
            context[gen_name].next();
        }

        // First, send the source to the `eval`
        context[gen_name].next( src );

        // Second, get the `eval` result
        return context[gen_name].next().value;
    };
}();
Run Code Online (Sandbox Code Playgroud)

现在你可以打电话了

var ctx = {};
limitedEval('var foo = "hello"', ctx);
limitedEval('alert(foo);', ctx);
Run Code Online (Sandbox Code Playgroud)

limitedEval现在,每个调用都将重用它在提供的ctx对象上找到的任何生成器函数.如果该函数不存在,它将创建它并将其放在ctx对象上.因为现在每个只有一个函数ctx,所以在使用var关键字创建变量时,它将重用函数的上下文.注意:您将无法通过ctx对象查找这些变量,因为它们只存在于函数的上下文中.

我不完全确定你是否可以在不使用发电机的情况下获得相同的结果.

编辑:其他人提出了很好的建议,所以我替换了随机生成的属性,Symbolwith(this)不是with(context)