防抖节流之总结篇

防抖节流之总结篇

前言

为了处理用户行为会频繁的触发事件执行,而对于DOM的操作、资源加载等耗费性能的处理,很可能会导致界面卡顿,甚至浏览器奔溃,而函数的节流与防抖就是为了解决类似需求而产生的。

防抖

概念:在短时间内多次触发同一个函数,只执行最后一次。
应用场景

  1. 表单输入验证
  2. 表单输入触发搜索 ajax
  3. resize/scroll/touch/mouseove 事件

原理
通过计时器延迟函数执行,短时间内再次触发时重置并添加新计时器。

实现
基础版:

function debounce(fn,wait = 1000){
	let timer = null;
	return function debounced(...args){
	//重新计算定时器
	if(timer){
		clearTimeout(timer);
	}
	//新定时器
	timer = setTimeout(()=>{
		fn(...args);
		timer = null		
	},wait)
  }
}

this指向原本变量版:

function debounce(fn,wait = 1000){
	let timer = null;
	return function debounced(...args){
	//重新计算定时器
	if(timer){
		clearTimeout(timer);
	}
	//新定时器
	timer = setTimeout(()=>{
		fn.apply(this,...args);
		timer = null		
	},wait);
  };
}

要求首次触发版:

function debounce(fn,wait = 1000, immediate = false){
	let timer = null;
	return function debounced(...args){
	//重新计算定时器
	if(timer){
		clearTimeout(timer);
	}
	//首次执行
	if (immediate && !timer) {
      fn.apply(this, ...args);

      timer = setTimeout(() => {
        timer = null;
      }, wait);

      return;
    }
    
	//新定时器
	timer = setTimeout(()=>{
		fn.apply(this,...args);
		timer = null		
	},wait);
  };
}

节流

概念:多次触发同一个函数,同一段时间内只执行一次。
应用场景

  1. 表单输入联想(常用)

原理
利用时间差(当前和上次执行)来过滤中间过程触发的函数执行。

实现
基础版:

function throttle(fn,wait = 1000){
	let prev = 0;

	const throttled = (...args)=>{
		const now = +new Date();
		if(now-prev>wait){
			fn.apply(...args);
			prev = now;
		}
	};
	return throttled;
}

全版:

	function throttle(fn) {
     let canRun = true; // 通过闭包保存一个标记
     return function () {
       if (!canRun) {
       		return;
       }
        // 在函数开头判断标记是否为 true,不为 true 则 return
       canRun = false; // 立即设置为 false
       setTimeout(() => { // 将外部传入的函数的执行放在 setTimeout 中
         fn.apply(this, ...arg);
         // 最后在 setTimeout 执行完毕后再把标记设置为 true(关键) 
         //表示可以执行下一次循环了。当定时器没有执行的时候
         //标记永远是 false,在开头被 return 掉
         canRun = true;
       }, 500);
     };
   }
   function aa(e) {
     console.log(e.target.innerWidth, e.target.innerHeight);
   }
   window.addEventListener('resize', throttle(aa));

区别

防抖是将一个周期内的多次操作通过重置计时器的方式合并到一次操作中,而节流是一个周期内只允许只执行一次操作,多余的操作将直接return false。

应用项目

为了更好的应用在实战中,于是我们把封装好的debounce和throttle函数放进我们的utils文件夹中吧~ (直接复制即可)

/**
 * 空闲控制 返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行
 *
 * @param  {function} func        传入函数,最后一个参数是额外增加的this对象,.apply(this, args) 这种方式,this无法传递进函数
 * @param  {number}   wait        表示时间窗口的间隔
 * @param  {boolean}  immediate   设置为ture时,调用触发于开始边界而不是结束边界
 * @return {function}             返回客户调用函数
 */
const debounce = function(func, wait, immediate) {
    let timeout, args, context, timestamp, result;

    const later = function() {
        // 据上一次触发时间间隔
        let last = Number(new Date()) - timestamp;

        // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
        if (last < wait && last > 0) {
            timeout = setTimeout(later, wait - last);
        } else {
            timeout = null;
            // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
            if (!immediate) {
                result = func.call(context, ...args, context);
                if (!timeout) {
                    context = args = null;
                }
            }
        }
    };

    return function(..._args) {
        context = this;
        args = _args;
        timestamp = Number(new Date());
        const callNow = immediate && !timeout;
        // 如果延时不存在,重新设定延时
        if (!timeout) {
            timeout = setTimeout(later, wait);
        }
        if (callNow) {
            result = func.call(context, ...args, context);
            context = args = null;
        }

        return result;
    };
};
/**
 * 频率控制 返回函数连续调用时,func 执行频率限定为 次 / wait
 *
 * @param  {function}   func      传入函数
 * @param  {number}     wait      表示时间窗口的间隔
 * @param  {object}     options   如果想忽略开始边界上的调用,传入{leading: false}。
 *                                如果想忽略结尾边界上的调用,传入{trailing: false}
 * @return {function}             返回客户调用函数
 */
const throttle = function(func, wait, options) {
    let context, args, result;
    let timeout = null;
    // 上次执行时间点
    let previous = 0;
    if (!options) options = {};
    // 延迟执行函数
    let later = function() {
        // 若设定了开始边界不执行选项,上次执行时间始终为0
        previous = options.leading === false ? 0 : Number(new Date());
        timeout = null;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
    };
    return function(..._args) {
        let now = Number(new Date());
        // 首次执行时,如果设定了开始边界不执行选项,将上次执行时间设定为当前时间。
        if (!previous && options.leading === false) previous = now;
        // 延迟执行时间间隔
        let remaining = wait - (now - previous);
        context = this;
        args = _args;
        // 延迟时间间隔remaining小于等于0,表示上次执行至此所间隔时间已经超过一个时间窗口
        // remaining大于时间窗口wait,表示客户端系统时间被调整过
        if (remaining <= 0 || remaining > wait) {
            clearTimeout(timeout);
            timeout = null;
            previous = now;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
            //如果延迟执行不存在,且没有设定结尾边界不执行选项
        } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
        }
        return result;
    };
};

欢迎关注我的博客: https://blog.csdn.net/weixin_42323607

github地址: https://github.com/NurTuam

多多支持!本人会持续更新哒 ❤️

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
防抖节流是前端中常用的优化性能的技术,可以有效地降低浏览器的负荷。这里将简单介绍防抖节流的概念,并提供几个示例代码,以供参考。 一、防抖 防抖(debounce)是指在一定时间内,多次触发同一个事件,只执行最后一次,以达到限制函数执行频率的目的。也就是说,防抖能够限制函数的执行次数,可以避免在短时间内频繁触发同一个事件导致程序崩溃。 1.1 示例代码 // debounce函数实现 function debounce(func, wait) { let timeout; return function() { const context = this; const args = arguments; clearTimeout(timeout); timeout = setTimeout(function() { func.apply(context, args); }, wait); }; } // 防抖函数使用示例 function handle() { console.log("scroll"); } window.addEventListener("scroll", debounce(handle, 1000)); 1.2 解释 在上述代码中,我们定义了一个debounce函数,并传入两个参数:一个是要执行的函数func,另一个是需要限定的事件触发时间wait。在这里我们使用了setTimeout函数,它会在wait毫秒后执行我们的func函数,如果再次触发事件,我们会使用clearTimeout()方法清除上一次执行的定时器,从而达到限制函数执行的目的。 二、节流 节流(throttle)是指在一段时间内,多次触发同一个事件,只执行一次,以达到限制函数执行次数的目的。不同于防抖,它可以使得函数定期执行,而不是一直等待到事件停止触发再执行。 2.1 示例代码 // throttle函数实现 function throttle(func, wait) { let timeout; let canRun = true; return function() { if (canRun) { canRun = false; func.apply(this, arguments); timeout = setTimeout(() => { canRun = true; }, wait); } else { clearTimeout(timeout); timeout = setTimeout(() => { func.apply(this, arguments); canRun = true; }, wait); } }; } // 节流函数使用示例 function handle() { console.log("scroll"); } window.addEventListener("scroll", throttle(handle, 1000)); 2.2 解释 在上述代码中,我们定义了一个throttle函数,并传入了两个参数:一个是要执行的函数func,另一个是需要限定的事件触发时间wait。在这个函数中,我们使用了canRun变量来控制函数可以执行的时间,在一个wait时间内,该变量的值为false,表示函数正在执行中,不会执行第二次。当wait时间过去后,我们才会将canRun设为true,允许函数再次被触发。如果在wait时间内再次触发事件,则会重置定时器,并再次等待wait时间后执行函数。 综上所述,防抖节流是前端中常用的技术,可以避免频繁触发同一个事件而导致程序崩溃,提高页面性能和用户体验。在实际开发中,我们需要根据具体情况选择合适的方法来进行优化,以达到最佳的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值