在准備前端筆試題的時候看到一段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(){…} )()內部定義的變量不會和外部的變量發生沖突,俗稱“匿名包裹器”或“命名空間”。
通過這些解釋,相信大家應該可以理解立即執行函數的用法了!