
嗯,写这篇博文的起因是群里有人问,为什么我的代码直接执行了,而没有等到他delay 5秒钟?
代码如下:
setTimeout(window.location.href = 'index.php?xxx=111', 5000)
当时,我回复:“你把你置入的代码用个匿名函数包起来就行了。”但这样的回复只是基于长期打代码的经验而已,并没有深入追究,写这篇博文算是为了弥补自己的知识漏洞。
关于setTimeout和setInterval,我们在认知上一直存在一个误区。如果我们认认真阅读过ECMAScript-262,我们会发现,这两个方法并没有在ECMA中被定义。
那么,他们到底来自哪里呢?翻阅MSDN和MDN对其的描述,我们可以看到如下内容:

嗯,他的规范来自于DOM文档,那我们继续观摩最新的HTML规范。
[NoInterfaceObject, Exposed=(Window,Worker)]
interface WindowTimers {
long setTimeout(Function handler, optional long timeout = 0, any... arguments);
long setTimeout(DOMString handler, optional long timeout = 0, any... arguments);
void clearTimeout(optional long handle = 0);
long setInterval(Function handler, optional long timeout = 0, any... arguments);
long setInterval(DOMString handler, optional long timeout = 0, any... arguments);
void clearInterval(optional long handle = 0);
};
Window implements WindowTimers;
我们发现接口windowTimers中重载了setTimeout和setInterval,使他们能够接受Function和DOMString两种不同的参数。
而当他们接受的参数为string类型时,实际上是执行了eval来解析我们所置入的代码。因此,我们在没有打引号的情况下,事实上是做了和:
eval(window.location.href = 'index.php?xxx=111');
差不多的操作,当然没有计时直接执行了。
而当我们将代码置入一个匿名函数中时,实际上是执行了一个回调,这样比直接置入字符串更加的安全。
分析了定义以后,我们就可以对setTimeout的用法有一个清晰的认识
function fn() {
alert(1);
}
setTimeout('fn()', 5000);
// after 5s ,alert 1;
function fn() {
alert(1);
}
setTimeout(fn, 5000);
// after 5s ,alert 1;
setTimeout(function () {
alert(1);
}, 5000);
// after 5s ,alert 1;
setTimeout('alert(1)', 5000);
// after 5s ,alert 1;
嗯,当然了,它还可以带参数写成:
setTimeout(func, delay, [param1, param2, ...]);
这样的形式,不过老生常谈的IE9以下是不支持的。
好了,说到这里,我们就应该注意到一个关键词“eval”,“eval”可以解析字符串形式的代码,而且是官方不推荐的代码解析方式,会有产生XSS漏洞的危险。那么作为可以在内部解析代码字符串的方法,我们需要给予比较大的关注。
同样能够解析代码的有以下一些对象和方法:
eval
setTimeout(String)
setInterval(String)
Function
execScript
setImmediate(String)
关于execScript,安利一篇老文章,点我查看
关于setImmediate,点我查看