這裏介紹一下函數防抖和函數節流,主要用js 舉例。當你看完的時候你就發現本身之前就用過,只是不知道它的專業術語,好吧,讓咱們來了解一下。閉包
什麼是函數防抖呢?app
假設在這樣一種狀況下,好比說咱們這樣那樣但願在滾動後,作某些操做,可是呢?函數
這裏分析一下,就是要在滾動後,什麼是滾動後呢?就是滾動不動了,那麼就是滾動後。post
咱們能夠監聽滾動事件:this
//給頁面綁定滑輪滾動事件 if (document.addEventListener) {//firefox document.addEventListener('onscroll', scrollFunc, false); } //滾動滑輪觸發scrollFunc方法 //ie 谷歌 window.onmousewheel = document.onscroll= scrollFunc; function scrollFunc{ //方法 }
若是咱們滾動一下就去執行咱們的事件,那麼就會形成不少事情,好比說如卡頓,再好比說執行屢次不符合咱們的預期,也就是功能沒有實現。firefox
那麼這個問題,就是由於咱們沒有作到滾動後。有些系統沒有提供滾動後的事件,那麼咱們得本身實現。code
那麼就得回到滾動後這個事件中來,滾動後就是滾動後一段時間內不動,那麼就是滾動後。事件
那麼能夠這樣寫:ip
function debounce(fn,wait){ var timer = null; return function(){ if(timer !== null){ clearTimeout(timer); } timer = setTimeout(fn,wait); } } function handle(){ console.log("滾動結束"); } window.addEventListener("onscroll",debounce(handle,1000));
這裏可能有你們困惑的一個問題,debounce 屢次執行,debounce 中的timer 沒有執行不是會爲空嗎?那麼(timer !== null) 不是不成立嗎?get
這裏就須要咱們看仔細了,咱們屢次執行的是:
function(){ if(timer !== null){ clearTimeout(timer); } timer = setTimeout(fn,wait); }
而不是debounce,由於閉包緣由,那麼他們共享一個timer,因此是這樣的了,一般共享一個timer 也是用閉包寫法否則,全局的話會污染的。
那麼這樣就是結束了,或者說debounce 是否完善了? 答案是否認的,在咱們的handle 並不能獲取到滾動的參數,好比說滾動的距離等,那麼咱們須要傳遞一下。
還有一個緣由就是this的問題,若是不這樣的話,this會變化的。
<div style="height: 200px;width: 200px;background-color: aqua;" id="test"></div> <script> function debounce(fn, wait) { var timer = null; return function () { let context = this; let args = arguments; if (timer !== null) { clearTimeout(timer); } timer = setTimeout(() => { fn.apply(context, args) }, wait); } } function handle() { console.log(this); } document.getElementById('test').onmousemove=debounce(handle, 1000); </script>
這個咱們獲得的this是
<div style="height: 200px;width: 200px;background-color: aqua;" id="test"></div>。
若是不使用apply,那麼是:Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
若是想詳細瞭解這一塊,須要去了解閉包這一塊,能夠去看個人閉包這一塊,或者網上搜。
function debounce(fn, wait) { var timer = null; return function () { let context = this; let args = arguments; if (timer !== null) { clearTimeout(timer); } timer = setTimeout(()=>{fn.apply(context,args)}, wait); } }
這樣就ok了。
在函數防抖中,還有另一種就是當即執行版。
當即執行的含義也是很是有意思的。仍是拿這個滾動說事。
我但願在滾動的時候立馬執行一個函數,可是呢,在以後繼續滾動過程當中,但願不要執行了。
function debounce(fn, wait) { var timer = null; return function () { let context = this; let args = arguments; let isCall = !timer; if (timer !== null) { clearTimeout(timer); } timer = setTimeout(() => { timer = null; }, wait); if(isCall){ fn.apply(context,args); } } }
實際上是這樣一個過程,若是有timer,那麼幹好清理timer的事,若是沒有timer 那麼執行須要調用的函數。
爲了咱們方便調用,能夠結合成一個:
function debounce(fn, wait, immediate) { var timer = null; return function () { let context = this; let args = arguments; if (timer !== null) { clearTimeout(timer); } if (immediate) { let isCall = !timer; timer = setTimeout(() => { timer = null; }, wait); if (isCall) { fn.apply(context, args); } } else { timer = setTimeout(() => { fn.apply(context, args); }, wait); } } }
immediate 設置是當即執行版,仍是延遲版。
那麼什麼函數節流呢?假若有這樣一個需求,有一個畫板,如今咱們有一個需求就是在畫畫的時候,每隔幾秒,保存一次當時的畫板的狀況。
那麼這個時候不能單純的settimerout,由於這裏的需求是畫畫的時候,也就是咱們在畫的時候。那麼這個時候咱們要監聽到手指移動事件,
而且幾秒執行一次。
function throttle(fn,wait){ var timer = null; return function(){ let context = this; let args = arguments; if(!timer){ setTimeout(() => { timer=null; fn.apply(context,args); }, wait); } } }
每次timer=null的時候咱們纔去設置settimeout ,這樣就行了。
固然記時方式有不少,咱們也可使用時間戳的方式。
function throttle(fn,wait){ var previous=0; return function(){ let context = this; let args = arguments; var now=Date.now(); if(now-previous>wait){ fn.apply(context,args); previous=now; } } }
通常在項目中,兩種通常取一個,而防抖通常都會用到,需求不同。