函數防抖和函數節流

前言

這裏介紹一下函數防抖和函數節流,主要用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;
		}
	}
}

通常在項目中,兩種通常取一個,而防抖通常都會用到,需求不同。

相關文章
相關標籤/搜索