创建一个变量并在沙盒中执行代码

6
我该如何通过Run()在Sandbox()中放置变量和运行代码?
function Sandbox() {
    this.test = 'insandbox';
}

Sandbox.prototype.Run = function(src) {
    eval.call(this, src);
};

Sandbox.prototype.getvar = function(name) {
    return this[name];
};

var bx = new Sandbox();
bx.Run('var x = 1;');
print(bx.getvar('test'))
print(bx.getvar('x'))        // undefined
print(x)

请不要回答eval()是不安全的,我不应该使用它。 请不要回答关于使用setters/getters的问题。
感谢您阅读!

我认为你在错误的作用域中。 - qwertymk
3个回答

3
这可能不是你想要的,但如果你传递一个函数而不是一个字符串到你的沙盒中,会怎么样呢?这甚至允许使用eval来处理源文件。
你的代码将像这样工作:
...

Sandbox.prototype.Run = function(fn){
    fn.call(this);
}

var bx = new Sandbox();
bx.run(function(){
    this.x = 1;
});
bx.getVar("x") // 1

如果你想使用eval,你只需要编写一个用于附加函数语法的函数即可。

/* source.js */
this.x = 1;
this.blah = "Hello, World!";

使用:

Sandbox.prototype.evaluate = function(src){
     return eval("function(){" + src + "}");
}

bx.Run(Sandbox.evaluate(src));
bx.getVar("x") // 1
bx.getVar("blah") // "Hello, World!"

此方法还可以通过将沙盒代码对象和函数传递给函数来传递,并模拟一个全新的伪环境来使用。

编辑:我对您问题的完美答案进行了一些研究,即:

“我能遍历在本地范围内声明的变量吗?”

答案是否(StackOverflow)。目前看来这是JavaScript的局限性。希望像这样的东西会在新规范中出现。

我目前的想法是评估源代码,使所有var语句都进入窗口对象,然后可以通过迭代并手动添加到Sandbox对象中。

像这样警告:极度简化
(function(){
    var src = get_source_file();
    eval(src);
    iterate_over_each_newly_created_window_property(function(property, value){
         bx[property] = value;
         window[property] = undefined;
    });
})();

编辑2: 我的想法可行=)

function Sandbox(){
    return this;
}
Sandbox.prototype.run = function(src){
    // Take a snapshopt of the window object before
    var before = {};
    var prop;
    for(prop in window){
        before[prop] = true;
    }
    // Then evaluate the source
    window.eval(src);
    // Then see what changed
    var changed = [];
    for(prop in window){
        if(!before[prop]){
            // Add to the sandbox object
            this[prop] = window[prop];
            delete window[prop];
        }
    }
}
var bx = new Sandbox();
bx.run("var x = 'Hello, World!';");
alert(bx.x);

一个工作示例(jsFiddle)

function Sandbox(){
this.keys = [];
this.values = [];
    return this;
}
Sandbox.prototype.eval = function(src){
    var before = {}, prop, fn;
    // Take a snapshopt of the window object before
    src = "function(" + this.keys.join(",") + "){" + src + "}";
    src = src.replace(/var/g, "");
    /* I'm not a wisard at regex so a better one should be used avoid this bug
    var x, y, z; */
    for(prop in window){
        before[prop] = true;
    }
    // Then evaluate the source
    fn = window.eval(src);
    fn.apply(window, this.values);
    // Then see what changed
    for(prop in window){
        if(!before[prop]){
            // Add to the sandbox object
            this.keys.push(prop);
            this.values.push(window[prop]);
            this[prop] = window[prop];
            delete window[prop];
        }
    }
}
var bx = new Sandbox();
bx.eval("var x = 1;");
bx.eval("var y = x;");
alert(bx.x);
alert(bx.y);

编辑3:修复了错误按照您的规格要求

现在我知道它里面有漏洞和一些可能会使它失控的功能。现在轮到你来清理代码以实现真正的使用了。我向您概述了如何完成这项工作。


这不是我在这种情况下可以使用的,但感谢您的支持! - calquin
不,这些变量应该放在 Sandbox 对象内部。 - calquin
这并不糟糕! :-) 我刚想到可以使用with()来实现。我肯定会重新审视这种方法,也许我们可以进一步优化它。我不喜欢window.eval() ;-) - calquin

2

这应该可以正常工作:

...

Sandbox.prototype.run = function(src) {
    eval(src);
};

...

bx.run('this.x = 1;');

...

bx.getvar('x') 将导致未定义。我希望在 Run() 表达式中不需要使用 'this'。 - calquin
1
如果你想让它工作,你需要使用thisvar创建一个本地变量,与Sandbox实例无关。 - Jordão
是的,这个方法可以工作。谢谢你。但是,我不能将其添加到每个表达式中,因为我不知道每次运行()时使用的确切类型的表达式。 - calquin
但是设置一个本地变量并使用getvar()返回本地变量不是可能的吗? - calquin
不,局部变量在其作用域结束时就消失了。而且该作用域不是“getvar”方法的一部分。 - Jordão

1

你可以使用闭包

Sandbox.prototype.Run = function(src) {
    (function(_src){
        eval.call(this, _src);
    })(src);
};

eval只能在全局作用域中运行。这是JavaScript的限制。 - Ilia Choly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接