1. 程式人生 > >關於for迴圈中引用setTimeout

關於for迴圈中引用setTimeout


題目1:

var a=[1,2,3];
		var len=a.length;
		for(___){
			setTimeout{function(){
				console.log(__);
			},0}
		}
		

要求輸出a的所有項。

該題目考察的就是JavaScript的單執行緒以及setTimeout的非同步特性。

【注】:JavaScript引擎是單執行緒執行的,瀏覽器執行期間只有一個執行緒在執行js程式。瀏覽器的核心是多執行緒的,他們在核心控制下相互配合,以保持同步,一個瀏覽器至少實現三個常駐執行緒:JavaScript引擎執行緒,GUI渲染執行緒,瀏覽器事件觸發執行緒。

  • JavaScript引擎是基於事件驅動單執行緒執行的,js引擎一直等待著任務佇列中任務的到來,然後加以處理,瀏覽器無論什麼時候都只有一個js執行緒在執行js程式。
  • GUI渲染執行緒負責瀏覽器介面的渲染,當介面需要重繪的時候或者由於某種操作引發迴流時,該執行緒就會執行,需要注意GUI渲染執行緒與js引擎是互斥的,當js引擎執行的時候GUI執行緒會被掛起,GUI更新會被儲存在一個佇列中,等到js引擎空閒時立即被執行
  • 事件觸發執行緒,當一個事件被觸發時該執行緒會把事件新增到待處理的佇列末尾,等待js引擎的處理。這些事件可來自js引擎當前執行程式碼塊,如setTimeout,也可來自瀏覽器核心的其他執行緒如滑鼠點選、ajax非同步請求等,但由於js的單執行緒關係所有這些事件都得排隊等待js引擎處理。
  • 當執行緒中沒有任何同步程式碼的前提下才會執行非同步程式碼。

--以上資訊來源於https://www.zhihu.com/people/fredshare。

在上題中setTimeout是非同步的程式碼,即使setTimeout中設定的等待時間為0也不會立刻執行,for迴圈程式碼是同步,所以要等待for迴圈執行完以後才會執行setTimeout。因此此時如果使用傳統的for迴圈,在setTimeout中列印a[i]的話會出現undefined的情況。此時可以在for迴圈外定義一個區域性變數j。列印的時候列印a[j++]就可以實現a陣列的遍歷

程式碼如下:

var a=[1,2,3,4];
			var i,len=a.length;
			var j=0;
			for(i = 0; i < len; i++) {
				(function(i) {
					setTimeout(function() {
						console.log(a[j++]);
					}, 0);
				})(i)
			}

或者
for(i = 0; i < len; i++) {
				(function(i) {
					setTimeout(function() {
						console.log(a[i]);
					}, 0);
				})(i)
			}

---------http://m.blog.csdn.net/article/details?id=51177648

題目2:

for(var i = 1; i <= 3; i++) {
                setTimeout(function() {
                    console.log(i)
                }, 0)
            }
            //4,4, 4

上面這個程式碼塊會列印三個 `4` 出來,而我們預想的結果是列印 1 2 3 。
之所以會這樣,是因為 setTimeout 中的 i 是對外層 i 的引用。當 setTimeout 的程式碼被解釋的時候,執行時只是記錄了 i 的引用,而不是值。而當 setTimeout 被觸發時,三個 setTimeout 中的 i 同時被取值,由於它們都指向了外層的同一個 i,而那個 i 的值在迭代完成時為 4,所以列印了三次 `4`。
為了得到我們預想的結果,我們可以把 i 賦值成一個區域性的變數,從而擺脫外層迭代的影響。
for (var i = 0; i <= 3; i++) {
  (function (idx) {
    setTimeout(function () {
      console.log(idx);
    }, 5);
  })(i);
}
或者:
var j=0; 
for(i = 0; i <=3; i++) {
			(function(i) {
				setTimeout(function() {
				console.log(a[j++]);
				}, 0);
			})(i)
		}