假期這麼快就結束了,其實對我來講沒什麼影響,由於我一週才兩節課,對於課多的同窗來講,我每天在休假,不要羨慕喲~ 但休假並不表明閒着,仍是得苦逼的編代碼,唉。。一入程序深似海。。ajax
無論學得多少,仍是總結一些,仍是一些小問題。固然也是很重要的,好! 廢話少說該入正題了。瀏覽器
上次提到異步,當時說,不知道是啥就去查漢語字典,但後來發現查了字典仍是不會。回顧一下併發
js哪些操做是異步的???setTimeout、setInterval、ajax、各類事件處理,才疏學淺,我就知道這些,誰還知道有哪些,勞煩告訴我,學習學習。dom
for(var i=0; i<5; i++){ setTimeout(function(){ console.log(i); },100); } //答案是: 5 5 5 5
for(var i=0; i<5; i++){ (function(j){ setTimeout(function(){ console.log(j); },100); })(i); }; 答案是 0 1 2 3 4
爲何是這個答案,重申一遍:做用域的關係。具體解釋:做用域與執行環境無關,由定義時決定並一步一步往上查找。上述兩個例子 執行匿名函數時執行的是:異步
function(){ console.log(i); } 而i等於多少? 咱們從定義處查找 setTimeout中沒有i 在往上一層就到了全局中,此時i已經等於5 因此答案是全是5 function(){ console.log(j); } 而j等於多少? 咱們仍是從定義處查找 setTimeout中沒有j 在往上一層就到了上一個形參爲j的匿名函數,此時j是形參,在定義setTimeout中的函數時,j的形參依次被傳入實參i,依次爲0,1,2,3,4 因此答案是全是0,1,2,3,4
換湯不換藥,找個例子實驗一下:函數
[1,2,3,4,5].forEach(function(elem){ setTimeout(function(){console.log(elem)}, 200); }) 答案是多少?? 5,5,5,5?? 1,1,1,1?? 1,2,3,4,5仍是??? 答案是1,2,3,4,5 若是錯了的話,再把前面的例子,文字看看。
繼續,再來一道:學習
for(var i=0; i<5; i++){ (function(j){ setTimeout(function(){ console.log(j);
}, Math.random()*1000); })(i); } //這個答案是什麼呢?? 是0,1,2,3,4仍是什麼??? 好好想一想。
根據前面的分析,先找定義處 依次往上查找,到function(j)這個函數時,已經把實參i傳進來了,因此答案是0,1,2,3,4 yes or no?? 答案是錯誤的。爲何?this
實參i確實把值傳進來了,該段代碼就等價於spa
setTimeout(function(){ console.log(0); }, Math.random()*1000); setTimeout(function(){ console.log(1); }, Math.random()*1000); setTimeout(function(){ console.log(2); }, Math.random()*1000); setTimeout(function(){ console.log(3); }, Math.random()*1000); setTimeout(function(){ console.log(4); }, Math.random()*1000);
此時只看這段代碼 答案是多少??? 你們確定會說是亂序的,跟 (Math.random()*1000) 值有關,yes 你答對了。 因此上面那個答案是亂序的0,1,2,3,4
那麼,下面那個代碼呢?設計
setTimeout(function(){ console.log(15); },100); setTimeout(function(){ console.log(5); },200);
不少人確定會說,這還用說嗎? 不用想都知道是15, 5。對,可是就這段代碼而言,這個15,5 從等待到執行(此處執行時間忽略不計)一共是花了300ms仍是200ms呢? 答案是200ms,爲何?剛剛開頭就說過,setTimeout()函數是異步的,異步有個的特色就是併發性,在同時定義這兩個函數時,他們同時在等待,放入到消息隊列中,因此100ms後第一個函數放入時,第二個函數已經等了100ms,因此兩個函數一共等了200ms。 總之一句話:異步具備併發性,與順序無關(時間相同或者相近的狀況下有關),與時間的快慢有關,請記住它。
這裏還要提的是:關於定時器中的時間,指的是什麼時候將定時器的代碼添加到隊列中,而不是什麼時候實際執行代碼,只能表示它會盡快執行。
如 :
document.onclick = function(){ setTimeout(function(){ console.log(34); },250); }; //若是onclick事件處理程序執行了300ms 那麼定時器代碼至少要在定時器設置後的300ms纔會被執行,也就是34至少要在300ms後輸出。
你們立刻就想到,若是是這樣的話,setInterval()就會出現一種狀況:在代碼再次被添加到隊列以前沒完成執行,致使定時器代碼連續運行好幾回,沒有停頓。幸虧,js引擎夠聰明,能避免這個問題,如何避免?當使用setInterval()時,僅當沒有該定時器的任何其餘代碼實例時,纔將定時器代碼添加到隊列中,這樣確保了定時器代碼加入到隊列中的最小時間間隔爲指定間隔,注意是添加到隊列中的最小時間間隔而不是執行。可是。。這個規則有兩個問題:1.某些間隔會被跳過,2.多個定時器的代碼執行之間的間隔可能會比預期的少。舉個例子:
某個onclick事件處理程序使用setInterval()設置了一個200ms間隔的重複定時器,若是事件程序花了300ms多一點的時間完成,定時器也花差很少的時間,就會出現上述兩個問題。
如圖: 此圖來自《js高級程序設計》這本書強烈推薦閱讀。。。
咱們分析一下:在5ms時建立了間隔爲200ms的定時器,第一個定時器在205ms後被添加到隊列中,但直到onclik執行完才執行,執行時,在405ms處第二個定時器又被添加到隊列中,在605ms第三個定時器要添加到隊列中,但此時第二個定時器還沒被執行,因此第三個不會被添加,同時在第一個定時器執行完以後第二個當即執行,因此第一個定時器和第二個定時器執行的間隔小於200ms,其實此處就是從第一個執行結束到第二個開始執行沒有間隔。
有人可能想到這樣的話js引擎並無解決定時器代碼連續運行問題,確實,但其實js引擎這種作法(在僅當沒有該定時器的任何其餘代碼實例時,纔將定時器代碼添加到隊列中),減小了連續的次數,不至於堆積太多。
爲了不這2個缺點,可使用以下模式使用鏈式setTimeout()調用。
setTimeout(function(){ //處理中 setTimeout(arguments.callee,interval); //arguments.callee 獲取對當前函數執行的引用。
//此處把須要處理的代碼寫在前面 有一個好處是 :下一個定時器必定是在前一個將要結束(此處能夠之直接視爲結束)才定義
}, interval);
每次函數執行時建立一個新的定時器,這樣的好處:在前一個定時器代碼執行完以前,不會向隊列插入新的定時器代碼,確保不會有任何缺失的間隔,並且,它能夠保證在下一次定時器代碼執行以前,至少要等待指定的間隔,避免了連續的運行。詳細請看《js高級程序設計》這本書。
關於setTimeout()函數還有一點就是:
你們都知道DOM比非DOM交互要更多的內存和CPU時間,若是連續進行過多的DOM相關操做可能會致使瀏覽器掛起甚至崩潰。如resize事件,爲了繞開這個問題咱們可使用setTimeout();
模式以下:
var processor = { timeoutId: null, performProcessing: function(){ //實際執行代碼 }, process: function(){
clearTimeout(this.timeoutId);
var that = this; //保存this,由於setTimeout()中用到的函數環境老是window
this.timeoutId = setTimeout(function(){ //timeoutId用來保存本次setTimeout的id以便下一次調用時清除
that.performProcessing();
}, 100);
}
};
processor.process();
時間間隔設爲100ms,表示最後一次調用process()以後至少100ms後纔會被調用performProcessing(),若是100ms以內調用了process()共20次,performProcessing()仍只會被調用一次。由於,在100ms以內定時器都沒開始執行,調用process()只會清除前一次的,最後只剩下最後一次setTimeout()。也就是說performProcessing()仍只會被調用一次。
這個過程叫作函數節流,基本思想是:某個代碼不能夠在沒有間斷的狀況連續重複執行。今天先暫且消化這些。
哪不對,或者要補充,推薦的強烈歡迎。。。