函数防抖和函数节流

前言

这里介绍一下函数防抖和函数节流,主要用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;
		}
	}
}

通常在项目中,两种通常取一个,而防抖通常都会用到,需求不同。

相关文章
相关标签/搜索