• JS: 防抖节流


    防抖节流

    1. 防抖(debounce)

      先来看看下面的代码:

      //触发滚动事件,num 就加1
      let num = 0;
      
      function incNum() {
      	console.log('鼠标滚动中');
          console.log(`${num++} ${Date().getSeconds()}s`);
      }
      
      window.addEventListener('scroll', incNum, false);
      

      当滚动鼠标,num会疯狂加1,从下图中可以看到稍微滚动一下鼠标就输出了N个num

      • 什么是防抖?

        防抖指的是让某些代码不可以在没有间断的情况下连续重复地执行。举个例子,一部电梯开门等待30s就会关门上升,在这30s的等待时间里,有人进来了,30s就重新从0开始计时,然后又有人进来了,30s就又重新计时,直到等待时间30s超时才会关门上升。

      • 实现防抖

        以上面的代码为例子来说明如何实现防抖:一直滚动鼠标,第一次触发scroll事件,在事件的执行函数里创建一个定时器,在指定的时间间隔后才运行相关代码。然后第二次触发scroll事件,如果此时前面设置的定时器还没开始执行,就清除前一次的定时器并重新设置一个。这么做的目的就是让scroll事件的执行函数在事件停止触发一段时间后才去执行,以此实现防抖。

        function debounce(func, delay) {
        	let timeout;
              
            return function(){
                // context 是为了绑定 this
            	let context = this;
                // args 是为了能正常使用事件对象 event
                let args = arguments;
                
                if (timeout) clearTimeout(timeout);
                timeout = setTimeout(()=> {
                    func.apply(context, args);
                }, delay);
            }
        }
        
        window.addEventListener('scroll', debounce(incNum, 1000), false);
        

        从下面图片可以看出,防抖处理后,只有在鼠标停止滚动一段时间后,才会输出num和秒数。

      • 取消执行

        当滚动鼠标后,在1s的等待时间里,突然想取消运行 scroll事件的函数了,该怎么办呢?

        答案是:在定时器没运行前,清除定时器。

        function debounce(func, delay) {
        	let timeout;
              
            let debounced = function(){
            	let context = this;
                let args = arguments;
                
                if (timeout) clearTimeout(timeout);
                timeout = setTimeout(()=> {
                    func.apply(context, args);
                }, delay);
            }
        	
            // 取消将要执行的定时器
        	debounced.cancel = function() {
                clearTimeout(timeout);
                timeout = null;
                console.log('已取消');
        	}
        
        	return debounced;
        }
        
        let testFunc = debounce(incNum, 1000);
        window.addEventListener('scroll', testFunc, false);
        // 在页面的按钮上绑定取消函数
        document.getElementById('cancel').addEventListener('click', testFunc.cancel, false);
        
      • 立即执行

        只要触发scroll事件,函数就立即执行,但必须要在函数执行完毕并过了一段时间后,再次滚动鼠标才会再次执行函数,而在等待时间里,如果滚动鼠标,等待时间会重新计时。

        function debounce(func, delay, immediate) {
        	let timeout;
              
            let debounced = function(){
            	let context = this;
                let args = arguments;
                
                if (timeout) clearTimeout(timeout);
                // immediate 为 true 就表示立即执行,忽略或者为 false 即非立即执行
                if (immediate) {
                	let callNow = !timeout;
                    timeout = setTimeout(()=> {
                    	timeout = null;
                	}, delay);
                    if (callNow) func.apply(context, args);
                } else {
                    timeout = setTimeout(()=> {
                    	func.apply(context, args);
                	}, delay);
                }
            }
        	
            // 取消将要执行的定时器
        	debounced.cancel = function() {
                clearTimeout(timeout);
                timeout = null;
                console.log('已取消');
        	}
        
        	return debounced;
        }
        
        window.addEventListener('scroll', debounce(incNum, 1000, true), false);
        
    2. 节流(throttle)

      • 什么是节流?

        还是电梯的例子:一部电梯开门等待30s就会关门上升,在这30s的等待时间里,有人进来了,计时继续累计,然后又有人进来了,计时依然累计,然后30s计时到了就立即关门上升(我还没上车呢.jpg)。

        简单来说,就是连续触发事件,但在固定时间内只执行一次函数。

      • 定时器实现

        // 定时器版本,鼠标滚动1s后才执行函数
        function throttle(func, delay){
            let timeout;
            return function(){
                let context = this;
                let args = arguments;
                if (!timeout) {
                    timeout = setTimeout(()=> {
                        func.apply(context, args);
                    }, delay);
                }
            }
        }
        
        window.addEventListener('scroll', throttle(incNum, 1000), false);
        

        疯狂滚动鼠标,但从下图可以看出函数只每秒执行一次。

      • 时间戳实现

        // 时间戳版本,鼠标滚动函数立即执行,间隔1s再执行下一次
        function throttle(func, delay) {
            let previous = 0;
            
            return function() {
                let now = Date.now();
                let args = arguments;
                if ( (now - previous) >= delay ) {
                    func.apply(this, args);
                    previous = now;
                }
            }
        }
        
        window.addEventListener('scroll', throttle(incNum, 1000), false);
        
      • 合并实现与取消

        将定时器和时间戳两个版本合并在一起,可以实现滚动鼠标,函数就立即执行,间隔1s再执行下一次,然后停止滚动鼠标后,还会多执行一次的效果。

        function throttle(func, delay) {
            let previous = 0;
            let timeout;
            let now;
            
            let throttled = function() {
                now = Date.now();
                let context = this;
                let args = arguments;
                if ( (now - previous) >= delay ) {
                    if (timeout) {
                        clearTimeout(timeout);
                        timeout = null;
                    }
                    previous = now;
                    func.apply(context, args);            
                } else if (!timeout) {
                    timeout = setTimeout(()=> {
                        previous = now;
                        timeout = null;
                        func.apply(context, args);
                    }, delay - (now - previous));
                }
            }
            
            // 取消函数
            throttled.cancel = function() {
                clearTimeout(timeout);
                previous = 0;
                timeout = null;
            }
            
            return throttled;
        }
        
        let testFunc = throttle(incNum, 1000);
        window.addEventListener('scroll', testFunc, false);
        // 在页面的按钮上绑定取消函数
        document.getElementById('cancel').addEventListener('click', testFunc.cancel, false);
        
  • 相关阅读:
    Mifare系列3-卡的能源和数据传递(转)
    Mifare系列2-非接触卡标准(转)
    Mifare系列1-简介(转)
    oot 空间不足解决方法
    C语言位操作(转)
    C语言面试题(三)
    C语言运算符和优先级
    C语言面试题(二)
    C语言面试题(一)
    Ubuntu+Win7双系统grub的修复问题
  • 原文地址:https://www.cnblogs.com/guolao/p/10113317.html
Copyright © 2020-2023  润新知