提示💡

  • 函数优化手段,处理高频触发事件。
  • 防抖:停止触发一段时间后执行。;节流:固定时间段内只执行一次。
  • 滚动、输入(搜索框)、点击、表单重复提交。

防抖和节流是前端开发中常用的函数优化手段,它们可以限制函数的执行频率,提升性能和用户体验。主要用于处理高频触发的事件,例如:用户的滚动、输入、点击和表单的重复提交等。它们都可以使用高阶函数实现。

防抖( 停止一段时间后触发,最后触发)

是什么

防抖:如果一个函数持续地、频繁地触发,那么只在它结束后过一段时间才开始执行。简而言之,防抖是在事件停止触发后延迟执行函数。

具体实现

实现防抖的基本方式是利用定时器,每次触发事件时都清除上一次的定时器,然后重新设置一个新的定时器。

  • 函数防抖的要点,也是需要一个 setTimeout 来辅助实现。延迟执行需要跑的代码。
  • 如果方法多次触发,则把上次记录的延迟执行代码用 clearTimeout 清掉,重新开始。
  • 如果计时完毕,没有方法进来访问触发,则执行代码。
// 防抖函数  
function debounce(func, wait) {  
	let timeout;  
	return function (...args) {  
	  const context = this;  
	  clearTimeout(timeout);  
	  timeout = setTimeout(() => func.apply(context, args), wait);  
	};  
}  

防抖如果需要立即执行,可加入第三个参数用于判断,实现如下:

function debounce(func, wait, immediate) {
 
    let timeout;
 
    return function () {
        let context = this;
        let args = arguments;
 
        if (timeout) clearTimeout(timeout); // timeout 不为null
        if (immediate) {
            let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发
            timeout = setTimeout(function () {
                timeout = null;
            }, wait)
            if (callNow) {
                func.apply(context, args)
            }
        }
        else {
            timeout = setTimeout(function () {
                func.apply(context, args)
            }, wait);
        }
    }
}

应用场景

防抖的应用场景:

  1. 输入框搜索:当用户在搜索框中输入关键字时,使用防抖可以避免频繁发送搜索请求,而是在用户停止输入一段时间后才发送请求,减轻服务器压力。
  2. 用户注册时候的手机号码验证和邮箱验证
  3. 按钮点击:当用户点击按钮时,使用防抖可以避免用户多次点击造成的多次提交或重复操作

手机号码验证和邮箱验证

只有等用户输入完毕后,前端才需要检查格式是否正确,如果不正确,再弹出提示语。以下还是以页面元素滚动监听的例子,来进行解析:

// 函数防抖
var timer = false;
document.getElementById("debounce").onscroll = function(){
    clearTimeout(timer); // 清除未执行的代码,重置回初始化状态
 
    timer = setTimeout(function(){
        console.log("函数防抖");
    }, 300);
};

节流( 固定时间内只触发一次,均匀触发)

是什么

节流:如果你持续触发事件,每隔一段时间,事件处理函数只执行一次。简而言之,节流是按照固定的时间间隔执行函数。

具体实现

节流的方式也是利用定时器,但与防抖不同的是,节流会保证在一个固定的时间间隔内至少执行一次事件处理函数

  • 函数节流的要点是,声明一个变量当标志位,记录当前代码是否在执行。
  • 如果空闲,则可以正常触发方法执行。
  • 如果代码正在执行,则取消这次方法执行。
// 节流函数  
function throttle(func, limit) {  
	let inThrottle;  
	return function (...args) {  
	  const context = this;  
	  if (!inThrottle) {  
		func.apply(context, args);  
		inThrottle = true;  
		setTimeout(() => inThrottle = false, limit);  
	  }  
	};  
}

重点

函数防抖的实现重点,就是巧用setTimeout做缓存池,而且可以轻易地清除待执行的代码。其实,用队列的方式也可以做到这种效果。这里就不深入了。

使用时间戳写法,事件会立即执行,停止触发后没有办法再次执行

function throttled1(fn, delay = 500) {
    let oldtime = Date.now()
    return function (...args) {
        let newtime = Date.now()
        if (newtime - oldtime >= delay) {
            fn.apply(null, args)
            oldtime = Date.now()
        }
    }
}

可以将时间戳写法的特性与定时器写法的特性相结合,实现一个更加精确的节流。实现如下

function throttled(fn, delay) {
    let timer = null
    let starttime = Date.now()
    return function () {
        let curTime = Date.now() // 当前时间
        let remaining = delay - (curTime - starttime)  // 从上一次到现在,还剩下多少多余时间
        let context = this
        let args = arguments
        clearTimeout(timer)
        if (remaining <= 0) {
            fn.apply(context, args)
            starttime = Date.now()
        } else {
            timer = setTimeout(fn, remaining);
        }
    }
}

应用场景

节流的应用场景:

  1. 页面滚动:当页面滚动时,使用节流可以限制滚动事件的触发频率,减少事件处理的次数,提高页面的响应性能。
  2. 按钮点击:当用户点击按钮时,使用节流可以减少事件处理的次数,避免过于频繁的操作。
  3. 窗口大小调整

页面滚动

函数节流应用的实际场景,多数在监听页面元素滚动事件的时候会用到。因为滚动事件,是一个高频触发的事件。以下是监听页面元素滚动的示例代码:

// 函数节流
var canRun = true;
document.getElementById("throttle").onscroll = function(){
    if(!canRun){
        // 判断是否已空闲,如果在执行中,则直接return
        return;
    }
 
    canRun = false;
    setTimeout(function(){
        console.log("函数节流");
        canRun = true;
    }, 300);
};

总结

区别

相同点:

  • 都可以通过使用 setTimeout 实现
  • 目的都是,降低回调执行频率。节省计算资源

不同点:

  • 函数防抖,在一段连续操作结束后,处理回调,利用clearTimeout和 setTimeout实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能
  • 函数防抖关注一定时间连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次

例如,都设置时间频率为500ms,在2秒时间内,频繁触发函数,节流,每隔 500ms 就执行一次。防抖,则不管调动多少次方法,在2s后,只会执行一次

如下图所示:

应用场景

防抖在连续的事件,只需触发一次回调的场景有:

  • 搜索框搜索输入。只需用户最后一次输入完,再发送请求
  • 手机号、邮箱验证输入检测
  • 窗口大小resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。

节流在间隔一段时间执行一次回调的场景有:

  • 滚动加载,加载更多或滚到底部监听
  • 搜索框,搜索联想功能

其他实现

装饰器

Hooks

扩展阅读