在准备前端笔试题的时候看到一段JavaScript脚本,对输出结果感到很疑惑,于是,研究了一下setTimeout的延时执行。脚本如下:
for(var i=1;i<=3;i++){
setTimeout(function(){
alert(i);
},0);
}
这段代码的输出结果是:弹出三次如下的对话框
看到效果感到无比郁闷,为什么呢?
于是,开始各种求救,终于搞懂了。。。下面跟大家分享一下。。。
关于setTimeout的延时执行,在这段代码中延迟时间为0ms,但是这并不代表setTimeout函数不延时执行。相反的,它仍然会改变函数的执行顺序。
1、实现javascript的异步;
正常情况下javascript都是按照顺序执行的。但是我们可能让该语句后面的语句执行完再执行本身,这时就可以用到setTimeout延时0ms来实现了。
如:
alert(1);
setTimeout("alert(2)", 0);
alert(3);
虽然延时了0ms,但是执行顺序为:1,3,2
这样就保证setTimeout里面的语句在某一代码段中最后执行。
正常情况下javascript都是按照顺序执行的。但是我们可能让该语句后面的语句执行完再执行本身,这时就可以用到setTimeout延时0ms来实现了。
如:
alert(1);
setTimeout("alert(2)", 0);
alert(3);
虽然延时了0ms,但是执行顺序为:1,3,2
这样就保证setTimeout里面的语句在某一代码段中最后执行。
因此,对于上面我们看到的程序,首先我们知道程序循环三次,每次循环的时候setTimeout函数都阻断了alert的弹出,
并将其放在最后执行,但最后一次循环结束后i的值为4,这时候执行三次setTimeout函数,其获取的i的值都为4,因此是弹出三次4!
不知道我解释的够不够通俗易懂,希望可以帮到大家!
2、阻断setTimeout的延时,维持JavaScript的同步;
如何让我们的脚本按照原有的顺序执行,输出1,2,3?下面我们来介绍一种方法来阻断setTimeout的延时执行,代码如下:
for(var i=1;i<=3;i++){
setTimeout((function(a){
//改成立即执行函数
alert(a);
})(i),0);
}
实现方法就是将setTimeout改成立即执行函数,
这个时候JavaScript的执行不在异步,将依次弹出1,2,3。
在这里对立即执行函数进行解释,(function(){…})()和(function(){…}())是两种javascript立即执行函数的常见写法,
要理解立即执行函数,需要先理解一些函数的基本概念。
函数声明、函数表达式、匿名函数:
(1)函数声明:
function fnName () {…};
使用function关键字声明一个函数,再指定一个函数名,叫函数声明。
(2)函数表达式:
var fnName = function () {…};
使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式,这是最常见的函数表达式语法形式。
(3)匿名函数:
function () {};
使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等
函数声明和函数表达式不同之处在于,一、Javascript引擎在解析javascript代码时会‘函数声明提升’当前执行环境(作用域)上的函数声明,而函数表达式必须的等到Javascript引擎执行到它所在行时,
才会从上而下一行一行地解析函数表达式,二、函数表达式后面可以加括号立即调用该函数,函数声明不可以。
下面举几个简单的例子,帮助理解:
(1)函数调用可以在函数声明之前 显示结果:
(2)函数调用不可以在函数表达式之前
(3)函数表达式后面加括号,当javascript引擎解析到此处时能立即调用函数。
( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。
通过这些解释,相信大家应该可以理解立即执行函数的用法了!