我有一个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中的范围和上下文但我无法解释这里的行为:
为什么我需要在“那个”上调用eval?根据互联网上的许多消息来源,全球定义功能的背景是最全球化的背景。 (这里应该是窗口)。通过eval评估的代码应该在调用eval函数的上下文中执行。
假设Ajax调用有一个新的全局上下文(是吗?)为什么我可以访问evalScript函数但不直接在这里评估脚本。
我的整体问题是:哪些特定规则适用于eval的使用?关于背景,我的职能在哪里?并且:示例中的Ajax调用原型是否有自己的全局对象?
答案 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)