开端
在最近理解的JS任务执行先后以及进程、线程、单线程、多线程的时候遇到一个问题,setInterval和setTimeout这种延时操作是在主函数代码执行后再执行,上代码1
<script>
for (var i = 0; i < 10; i++) {
setTimeout((function (j) {
return function () {
console.log(j);
}
})(i),10);
console.log("----");
}
</script>
这种在定时器中调取闭包的方法是为了把每一次的i都按0-9遍历出来。
那么以下这样写跟上面的代码是一样吗?代码2
<script>
for (var i = 0; i < 10; i++) {
setTimeout((function (j) {
//return function () { 不按常规闭包形式返回函数体
console.log(j);
//}
})(i),10);
console.log("----");
}
</script>
想不到吧!
那么接下来要说说SetInterval(code,time)的第一个参数传入不同值得不同形式(SetTimeout同理)
正常来讲我们在定时器中第一个参数肯定是回调函数,自然而然就是function类型,代码1也很正常按着js的任务处理优先级,先处理for循环及for循环里面的任务代码,再处理定时器或者点击事件等操作,so,结果自然是先10个“________”然后再定时器。
在代码2中,return返回出来的不是一个函数,而是一个代码段,那么很明显违背了定时器的初衷第一个函数为function,在浏览器的结果看来,这个定时器是失效了,就简单认为是一个普通的代码段运行没有定时器的效果。
-------------------------------------
以下是总结了一些网上的案例去分析定时器第一个参数的种种问题
分三种情况
function fun(){
alert(1);
}
setInterval(fun,1000);
//正确
setInterval(fun(),1000);
//调用函数正常,setInterval调用出错
setInterval("fun()",1000);
//全局作用域下正常执行
第一种毋庸置疑,第一个参数就是一个函数
第二种写法就是跟上面代码2一样的道理,定时器已经失效,具体是什么原因我也不能道其然
第三种写法就特殊了,其中这种加引号的调用就可以理解为可执行代码,就想eval(动态执行代码)一样去执行第一个参数,就是对fun方法的调用 理所当然的弹出 1,一秒钟间隔,一直执行。
eval("console.log(1);"); //1
定时器中的第一个函数加上双引号,就相当于eval的动态执行代码,直接运行。
拓展
关于定时器中“fun()”
window.onload = function(){
function fun(){
alert(1);
}
setInterval(fun,1000);
//正确
setInterval(fun(),1000);
//调用函数正常,setInterval调用出错
setInterval("fun()",1000);
//报错fun()未定义
}
setInterval("fun()",1000); 这种调用报未定义,在全局我们已经说过了 。我们可以把带引号的参数理解为 可执行代码 。
而setInterval现在把以引号包括的可执行代码进行处理。就像eval一样给予执行。其在执行中 fun() 执行环境发生了变化,不是在window.onload方法下,而是在全局环境中也就是window.大家应该知道JavaScript存在作用域链,由内向外依次查找。内部可以访问其上层的函数和变量,而外部却不能访问内部的函数和变量。JavaScript有一个预编译处理,首先对函数和变量进行预编译。也就是说其函数和变量作用域是在其声明的时候确定的,而不是在执行的时候确定。当setInterval把"fun()"执行环境换为全局(详细见:拓展)的后,对fun的调用是无效的。因为全局不能访问局部的函数和变量。window.onload相对于window来说就是局部的。
setInterval("var a=1;var b=2;c=a+b;alert(c);",1000);//如果你看完上面内容,这里的答案呼之欲出