zoukankan      html  css  js  c++  java
  • 函数防抖和函数节流

    前言

    这里介绍一下函数防抖和函数节流,主要用js 举例。当你看完的时候你就发现自己以前就用过,只是不知道它的专业术语,好吧,让我们来了解一下。

    正文

    函数防抖

    什么是函数防抖呢?

    假设在这样一种情况下,比如说我们这样那样希望在滚动后,做某些操作,但是呢?

    这里分析一下,就是要在滚动后,什么是滚动后呢?就是滚动不动了,那么就是滚动后。

    我们可以监听滚动事件:

    //给页面绑定滑轮滚动事件 
    if (document.addEventListener) {//firefox 
    document.addEventListener('onscroll', scrollFunc, false); 
    } 
    //滚动滑轮触发scrollFunc方法 //ie 谷歌 
    window.onmousewheel = document.onscroll= scrollFunc;
    function scrollFunc{
       //方法
    }
    

    如果我们滚动一下就去执行我们的事件,那么就会造成很多事情,比如说如卡顿,再比如说执行多次不符合我们的预期,也就是功能没有实现。

    那么这个问题,就是因为我们没有做到滚动后。有些系统没有提供滚动后的事件,那么我们得自己实现。

    那么就得回到滚动后这个事件中来,滚动后就是滚动后一段时间内不动,那么就是滚动后。

    那么可以这样写:

    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) 不是不成立吗?

    这里就需要我们看仔细了,我们多次执行的是:

    function(){
    	if(timer !== null){
    		clearTimeout(timer);
    	}
    	timer = setTimeout(fn,wait);
    }
    

    而不是debounce,因为闭包原因,那么他们共享一个timer,所以是这样的了,通常共享一个timer 也是用闭包写法不然,全局的话会污染的。

    那么这样就是结束了,或者说debounce 是否完善了? 答案是否定的,在我们的handle 并不能获取到滚动的参数,比如说滚动的距离等,那么我们需要传递一下。

    还有一个原因就是this的问题,如果不这样的话,this会变化的。

      <div style="height: 200px; 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; 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;
    		}
    	}
    }
    

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

  • 相关阅读:
    XPath在python中的高级应用
    Python中 sys.argv[]的用法简明解释
    python format
    爬虫解析:XPath总结
    c#attribute特性
    .net随笔--不好归类的
    windows系统操作
    linux学习
    visual studio各种新建项目和新建项简介
    自定义界面和控件--基础
  • 原文地址:https://www.cnblogs.com/aoximin/p/14296740.html
Copyright © 2011-2022 走看看