在js中,setTimeout()函數是全域變量,無論在哪裡它都是全域變數而不是局部變數。所以下面這個例子的結果可能會讓你大吃一驚:
function A() { this.b = function() { console.log(1) } } var c = new A() setTimeout(c.b, 10)
可能有些人認為上面這個例子輸出的結果是1,但是結果是undefined。這是因為setTImeout()函數是全域變量,它把裡面的c.b發到全域變數中,而全域變數中沒有b這個屬性,所以回傳的undefined。
在js中,setTimeout()函數的運作機制是這樣的,setInterval()函數也是一樣。將指定的程式碼移出本次執行,等到下一輪Event Loop時,再檢查是否到了指定時間。如果到了,就執行對應的程式碼;如果不到,就等到再下一輪Event Loop時重新判斷。這意味著,setTimeout指定的程式碼,必須等到本次執行的所有程式碼都執行完,才會執行。 ·
每回合Event Loop時,都會將「任務佇列」中需要執行的任務,一次執行完畢。 setTimeout和setInterval都是把任務加入到「任務佇列」的尾端。因此,它們實際上要等到目前腳本的所有同步任務執行完,然後再等到本次Event Loop的「任務佇列」的所有任務執行完,才會開始執行。由於前面的任務到底需要多少時間執行完,是不確定的,所以沒有辦法保證,setTimeout和setInterval指定的任務,一定會按照預定時間執行。
所以如果前面有一個函數被阻塞了,就一直等待函數執行完才能運作。因為js是單線程的,所以比較容易會發生阻塞的。
在setTimeout()函數中,最快的運作時間是4毫秒,即使將延遲參數寫為0或負數,它都不會立刻的運作。在延遲參數為0或負數時,需要等待當前腳步的同步任務和event loop(即任務隊列)執行完畢才開始執行,由於參數為0或負數,只是比其他setTimeout()函數盡可能早的運行函數,而參數為0或負數本身優先權相同。如:
setTimeout('console.log(1)',0) console.log(2)
回傳的結果是2 1
setTimeout('console.log(1)',0) setTimeout('console.log(2)',-1) setTimeout('console.log(3)',-1) setTimeout('console.log(4)',0)
回傳的結果是1 2 3 4
在setTimeout()中,還有一點值得注意的。那就是它的第一個參數必須是需要編譯的程式碼或是函數方法。所以像上面的例子中,console.log()被單引號括起來變成一個字串,在setTimeout中有一個eval()函數可以將字串進行編譯。如果我們將console.log()方法沒有用單引號括起來,那麼整個setTimeout()函數就會立即執行,而不會考慮有沒有延遲參數。如我在美的的美雲智數面試遇見過的這一題一樣:
console.log(1) setTimeout(console.log(2),0) console.log(3) setTimeout(console.log(4),-1)
返回的結果是1 2 3 4,如果將單引號括起來,返回的結果是1 3 2 4
在js中想要清除setTimeout計時器,需要clearTimeout()這個函數。具體用法如下所示:
var a = setTimeout('console.log(1)', 1) clearTimeout(a)
最後貼出一道題看看畢業了沒有,對比下面三段代碼,看分別輸出上面:
for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); }, 0); console.log(i); }
for (var i = 0; i < 3; i++) { setTimeout((function() { console.log(i); })(i), 0); console.log(i); }
for (var i = 0; i < 3; i++) { setTimeout((function(i) { return function() { console.log(i); }; })(i), 0); console.log(i); }
這道題設計了一些js的作用域的問題,我會特別寫一篇文章介紹es5中作用域和es6中的作用域發生了哪些變化。
以上是js中的setTimeout()函數的詳細內容。更多資訊請關注PHP中文網其他相關文章!