范围和评估说明

时间:2011-08-03 12:53:39

标签: javascript prototypejs eval scope

我有一个Ajax调用,我将一些JavaScript作为String返回。在onSuccess方法中,我想评估此代码。在JavaScript代码中有函数声明。所有这些功能都应该在eval之后可以访问。

我编写了一个尽可能小的例子。 (事情正在示例中的onFailure方法中进行,因为在JFiddle中我无法成功进行Ajax调用)。

您可以在此处找到示例:http://jsfiddle.net/ubXAV/6/

您看到的示例适用于所有浏览器(不幸的是,这在IE中的JSFiddle中不起作用)。我标记了一些引用下面问题的行。这是代码:

function evalScript(script)
{
   that.eval(script); //1.
}

var that = this;

//  AJAX-Call - GadgetActionServlet
new Ajax.Request("THISWILLFAIL.com", {
    method: 'post',
    onSuccess: function(ajaxResponse) {
            alert("success");
    },
    onFailure: function(){
        var script = "{function sayHello(){alert('Hello');}}";
        //that.eval(script); //not working in IE 2.
        evalScript(script);  //working in all browsers
    }
});

我在互联网上阅读了很多关于java中的范围和上下文但我无法解释这里的行为:

  1. 为什么我需要在“那个”上调用eval?根据互联网上的许多消息来源,全球定义功能的背景是最全球化的背景。 (这里应该是窗口)。通过eval评估的代码应该在调用eval函数的上下文中执行。

  2. 假设Ajax调用有一个新的全局上下文(是吗?)为什么我可以访问evalScript函数但不直接在这里评估脚本。

  3. 我的整体问题是:哪些特定规则适用于eval的使用?关于背景,我的职能在哪里?并且:示例中的Ajax调用原型是否有自己的全局对象?

2 个答案:

答案 0 :(得分:4)

首先关闭:如果您可以避免使用eval,请避免使用eval。您的代码是否POST返回?因为如果您愿意使用GET,您只需在页面中添加一个脚本元素:

var script = document.createElement('script');
script.src = "http://example.com" +
                 "?" + encodeURIComponent("param1name") + "=" + encodeURIComponent("param1value") +
                 "&" + encodeURIComponent("param1name") + "=" + encodeURIComponent("param2value");
var parent = document.body
             || document.documentElement
             || document.getElementsByTagName('head')[0];
parent.appendChild(script);

完成。

或者如果它必须是POST,那么真的是否必须是实际的脚本代码?难道不是由页面上的代码解释的数据吗?如果您可以这样做,JSON是一种有用的数据格式。

但是,如果 POST,并且你得到的回复 是实际的脚本代码而不是数据,那么我们将拥有做eval之类的事情。 : - )

eval本身非常非常特别。它在它使用的范围内工作,即使它看起来有点像函数,而不是函数的工作方式。所以实际上在全局范围内评估脚本代码很难,除非eval调用实际上是在全局范围(不在任何函数调用中),当然你不能在这里做 - 你必须从你的ajax回调中触发这个,所以根据定义,这发生在一个函数中。 (编辑:我只想到了一种在全局范围内实际使用eval的方法,从函数内部开始。请参阅答案末尾的更新。但它是邪恶的,可怕的和错误的。)

您可能已经看到使用window.eval的建议的原因是许多现代浏览器提供window.eval(而不是eval)来评估全局范围内的给定代码。但它并不适用于所有浏览器,当然也不适用于较旧的浏览器。

但是有一些解决方法。 IE系列提供的execScript 非常类似于其他浏览器提供的window.eval,在最糟糕的情况下,您可以使用script元素。这是一个全局的eval函数,几乎适用于所有内容:

window.evalInGlobalScope = (function() {
    var fname, scr;

    // Get a unique function name
    do {
        fname = "__eval_in_global_test_" + Math.floor(Math.random() * 100000);
    }
    while (typeof window[fname] !== 'undefined');

    // Create test script
    scr = "function " + fname + "() { }";

    // Return the first function that works:
    return test(evalInGlobalScope_execScript) ||
           test(evalInGlobalScope_windowEval) ||
           test(evalInGlobalScope_theHardWay) ||
           evalInGlobalScope_fail;

    function test(f) {
        try {
            f(scr);
            if (typeof window[fname] === 'function') {
                return f;
            }
        }
        catch (e) {
            return false;
        }
        finally {
            try { delete window[fname]; } catch (e) { window[fname] = undefined; }
        }
    }
    function evalInGlobalScope_execScript(str) {
        window.execScript(str);
    }
    function evalInGlobalScope_windowEval(str) {
        window.eval(str);
    }
    function evalInGlobalScope_theHardWay(str) {
        var parent, script, d = document;

        parent = d.body || d.documentElement || d.getElementsByTagName('head')[0];
        if (parent) {
            script = d.createElement('script');
            script.appendChild(d.createTextNode(str));
            parent.appendChild(script);
        }
    }
    function evalInGlobalScope_fail() {
        throw "evalInGlobalScope: Unable to determine how to do global eval in this environment";
    }
})();

..和here's a live example of using it

请注意,确定使用内容的所有代码只运行一次;已选择的函数将分配给evalInGlobalScope上的window属性。

另请注意,我没有给它任何返回值。那是因为“硬路”版本基本上不能返回任何返回值,所以如果它们都没有,那么它是最安全的。请注意,我不确定哪些浏览器仍然需要“艰难的方式” - 现在几乎所有浏览器都有execScript和/或window.eval


更新:我在上面说过,您无法在函数中使用全局范围内的eval。从技术上讲,这是真的,但我想到了一种绕它运行的方法。这是邪恶的,可怕的和错误的,但它确实有效:改为使用setTimeout,并将其超时0

setTimeout("your code here", 0);

当你给setTimeout一个字符串时,它会在其上执行eval - 超时后,在全局范围内

同样,它是邪恶的,可怕的和错误的,并且它具有额外的缺点,即它是异步的(而对于我们的evalInGlobalScope函数,eval同步发生),但它确实......有点......工作。 (Live copy)我推荐它。

答案 1 :(得分:2)