菜鳥理解setTimeout和setInterval

寫在前面,最近在準備校招,陸陸續續作一些以前的總結,寫了一個小系列的文章,想借此機會記錄下來,也能之後有個地方能進行查閱,上一篇文章在css基礎總結但願能幫助一下和我同樣的菜鳥們好了,正文開始。javascript

這是一個老生常談,新手掉坑的問題,算是一個比較經典的對於javascript運行機制的理解問題,我在這裏粗淺的談一下本身的理解,話很少說,進入正題:css

二者表面上的區別

  1. setTimeout() 方法用於在指定毫秒數以後調用其中的函數html

  2. setInterval() 方法則是在間隔必定毫秒後重復調用其中的函數java

透過現象看本質

時間精確問題

因爲js是運行在單線程的環境當中的,單線程就意味着任務的執行須要依賴任務隊列。實際運行時是將兩個方法的代碼塊移出當前運行環境(從任務隊列移出到回調隊列中),當執行完當前任務後,檢查回調隊列中有無須要執行的任務(對應這兩個方法爲是否已經到執行時間),但是若是時間到時剛好有別的任務在進行的話,因爲其單線程的機制,該方法就只能等到當前任務結束以後才能運行。segmentfault

回到方法自己,這就至關於其餘的正常任務在一個隊列中,當遇到這兩個方法時,就將他們移出隊列,並開始計時,當時間到時,直接「插隊」到隊首,若是隊首有正在執行的任務,則排在次隊首,等待執行。也就是說,這僅僅是「計劃」在將來某一個時間執行某個任務,並不能保證精確的時間。閉包

setInterval重複執行問題

這個方法執行時僅當沒有該計時器的其餘代碼示例時才進行下一輪的執行。這樣的規則就會致使某些間隔會被跳過,同時多個間隔可能比預期時間要短。因此爲了不setInterval所形成的問題,能夠用setTimeout來經過循環代替setInterval方法,從而實現一個重複的定時器(除非必要,儘可能避免代碼中出現setInterval)函數

方法中使用this的問題

在兩個方法中傳入函數時(即第一個函數參數中含有另一個函數),此函數中的this會只想window對象。這是因爲兩個方法調用的代碼在與所在函數徹底分離的執行環境上(第一條中有講到的兩個方法的運行機制),這就會致使這些代碼中包含的this關鍵字會指向window(或全局)對象。this

可是要注意,若是this只是在兩個方法中而不是在方法中的函數中時,this的指向符合咱們的預期爲當前對象。prototype

解決方法:

1.將當前對象的this存爲一個變量,定時器內的函數利用閉包來訪問這個變量,以下:線程

var num = 0;
function Obj (){
    var that = this;    //將this存爲一個變量,此時的this指向obj
    this.num = 1,
    this.getNum = function(){
        console.log(this.num);
    },
    this.getNumLater = function(){
        setTimeout(function(){
            console.log(that.num);    //利用閉包訪問that,that是一個指向obj的指針
        }, 1000)
    }
}
var obj = new Obj;
obj.getNum();          //1  打印的爲obj.num,值爲1
obj.getNumLater()      //1  打印的爲obj.num,值爲1

2.利用bind()方法:

var num = 0;
function Obj (){
    this.num = 1,
    this.getNum = function(){
        console.log(this.num);
    },
    this.getNumLater = function(){
        setTimeout(function(){
            console.log(this.num);
        }.bind(this), 1000)    //利用bind()將this綁定到這個函數上
    }
}
var obj = new Obj;
obj.getNum();                 //1  打印的爲obj.num,值爲1
obj.getNumLater()             //1  打印的爲obj.num,值爲1

bind()方法是在Function.prototype上的一個方法,當被綁定函數執行時,bind方法會建立一個新函數,並將第一個參數做爲新函數運行時的this。在這個例子中,在調用setTimeout中的函數時,bind方法建立了一個新的函數,並將this傳進新的函數,執行的結果也就是正確的了。關於bind方法可參考 MDN bind

清除計時器

clearTimeout()

在在使用setTimeout時,該方法會返回一個惟一的關於當前計時器的計時ID,在clearTimeout()方法中傳入這個ID值便可取消對應的Timeout

clearInterval()

同上

參考

上面是我在使用過程當中遇到問題後在網上查閱後本身的一些總結,但願對和我同樣的新手有所幫助,想要更深刻了解他們的區別和js的一些運行機制,請入傳送門:

傳送門1---阮大的剖析

傳送門2---this指向

傳送門3---調用執行

相關文章
相關標籤/搜索