JavaScript——防抖与节流

JavaScript——防抖与节流

1 什么是 JS 防抖与节流?

在进行窗口的 resize、scroll以及输入框内容校验等操作时,这些事件对应的处理函数被调用的频率会非常高,浏览器的响应速度可能低于事件的触发频率,从而出现延迟或卡顿现象,降低用户的体验。节流和防抖作为页面性能优化的一种策略,可以降低回调函数的执行频率,节省计算资源,有效减少浏览器引擎的损耗,保证用户的体验。

函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果在设定的时间到来之前,事件再次被触发,则重新设定延时时间。

函数节流(throttle):当持续触发事件时,事件处理函数执行一次,如果在设定的时间到来之前,事件再次被触发,事件处理函数则不会执行,或者在设定的时间段最后一刻执行一次,保证一定时间段内只调用一次事件处理函数。

2 函数防抖(debounce)

2.1 实现方式

每次触发事件时先清除之前的延时调用方法(clearTimeout()),然后设置一个新的延时调用方法(setTimeout())。

2.2 使用场景

  1. 用户短时间内重复点击按钮提交;(例如:登录、发布任务、点赞与取消点赞等)
  2. 输入框搜索联想事件;

2.3 缺点

如果事件在规定的时间间隔内不断被触发,则事件处理函数的执行则会不断地延迟。

2.4 函数实现及其演化

2.4.1 简单实现
//简单的防抖函数
function debounce(func, wait) {
    
    
    //定时器变量
    let timeout;
    return function () {
    
    
        //每次触发scroll,先清除定时器
        clearTimeout(timeout);
        //wait秒后触发事件操作func   (setTimeout返回此定时器的编号)
        timeout = setTimeout(func, wait);
    };
};
//绑定在scroll事件上的handlerFunc
function handlerFunc() {
    
    
    console.log('Success');
}


//没采用防抖动
window.addEventListener('scroll', handlerFunc);
//采用防抖动
window.addEventListener('scroll', debounce(handlerFunc, 1000));
2.4.2 this 指向、event 绑定问题
//以闭包的形式返回一个函数,内部解决了this指向的问题和event对象传递的问题
function debounce(func, wait) {
    
    
    let timeout;
    return function () {
    
    
    	let context = this;
      	let args = arguments;
        clearTimeout(timeout);
        timeout = setTimeout(function () {
    
    
            func.apply(context, args);
        }, wait);
    };
}
2.4.3 立即触发问题
//首次触发执行,再次触发以后开始执行防抖函数
//使用时不用重复执行这个函数,本身返回的函数才具有防抖功能
function debounce(func, wait, immediate) {
    
    
    let timeout;
    return function () {
    
    
        let context = this;
        let args = arguments;
        
        if(timeout) clearTimeout(timeout);
        //是否在某一批事件中首次执行
        if (immediate) {
    
    
            let callNow = !timeout;
            timeout = setTimeout(function () {
    
    
                timeout = null;
                func.apply(context, args);
                immediate = true;
            }, wait);
            if (callNow) {
    
    
                func.apply(context, args);
            }
            immediate = false;
        } else {
    
    
            timeout = setTimeout(function () {
    
    
                func.apply(context, args);
                immediate = true;
            }, wait);
        }
    }
}
2.4.4 返回值问题
function debounce(func, wait, immediate) {
    
    
    let timeout, result;
    return function () {
    
    
        let context = this;
        let args = arguments;
        
        if (timeout) clearTimeout(timeout);
        
        if (immediate) {
    
    
            let callNow = !timeout;
            timeout = setTimeout(function () {
    
    
                result = func.apply(context, args);
            }, wait);
            if (callNow) {
    
    
                result = func.apply(context, args);
            }
        } else {
    
    
            timeout = setTimeout(function () {
    
    
                result = func.apply(context, args);
            }, wait);
        }
        return result;
    }
}

3 函数节流(throttle)

3.1 实现方式

每次触发事件时,如果当前有等待执行的延时函数,则直接 return。

3.2 使用场景

  1. 调整浏览器窗口大小 resize、页面滚动加载 scroll;
  2. 图片懒加载;

3.3 函数实现

3.3.1 定时器方式
//首次不会立即执行,最后一次会执行,和时间戳方式互补
function throttle(func, wait) {
    
    
    let timeout;
    return function () {
    
    
        let context = this;
        let args = arguments;
        if (!timeout) {
    
    
        	timeout = setTimeout(function () {
    
    
                func.apply(context, args);
                timeout = null;
            }, wait);    
        }
    }
}
3.3.2 时间戳方式
//在开始触发时会立即执行一次,和定时器方式互补
function throttle(func, wait) {
    
    
    let pre = 0;
    return function () {
    
    
        let context = this;
        let args = arguments;
        let now = +new Date();
        if (now - pre > wait) {
    
    
            func.apply(context, args);
            pre = now;
        }
    }
}

4 示例代码

4.1 微信小程序示例

  1. 在根目录下的 utils 包中创建 functionUtil.js

    /**
     * 可以第一次立即触发的防抖函数
     * @param {*} fn 待防抖函数
     * @param {*} interval 间隔时间
     */
    export function debounce(fn, interval) {
          
          
      let timer;
      let gapTime = interval || 1000;	//间隔时间,如果未传入interval,则默认1s
      return function() {
          
          
        clearTimeout(timer);
        let context = this;
        let args = arguments;			//保存此处的arguments,因为setTimeout是全局的,arguments不是防抖函数需要的
        timer = setTimeout(function() {
          
          
          fn.call(context,args);
        }, gapTime);
      };
    }
    /**
     * 节流函数
     * @param {*} fn 待节流函数 
     * @param {*} interval 间隔时间
     */
    export function throttle(fn, interval) {
          
          
      let enterTime = 0;              	//触发的时间
      let gapTime = interval || 1000 ;	//间隔时间,如果未传入interval,则默认1s
      return function() {
          
          
        let context = this;
        let backTime = new Date();    	//第一次函数return即触发的时间
        if (backTime - enterTime > gapTime) {
          
          
          fn.call(context, arguments);
          enterTime = backTime;       	//赋值给第一次触发的时间,这样就保存了第二次触发的时间
        }
      };
    }
    
  2. 在需要调用的 js 文件中引入并调用

    import {
          
          
      debounce,  
      throttle
    } from "../../utils/functionutil";
    
    Page({
          
          
        data: {
          
          },
        
        // 调用防抖函数
        functionX: debounce(function() {
          
          
        	this.handlerFunc1();
    	}, 1500),
        
        // 调用节流函数
        functionY: throttle(function() {
          
          
        	this.handlerFunc2();
      	}, 1500),
        
        handlerFunc1() {
          
          },
        
        handlerFunc2() {
          
          }
    })
    

5 参考资料

  1. JS—节流与防抖_js节流防抖_傲娇味的草莓的博客-CSDN博客

  2. js防抖和节流 区别及实现方式_木子Leo的博客-CSDN博客_防抖节流区别

  3. 什么是节流与防抖? - 知乎 (zhihu.com)

  4. 微信小程序 用函数实现 防抖 和 节流 - 简书 (jianshu.com)

猜你喜欢

转载自blog.csdn.net/Alan_Walker688/article/details/128674078