手写节流和防抖

一、什么是防抖

定义: 用于限制某个函数在短时间内被频繁调用的情况

特点:

  • 延迟执行:防抖会延迟执行目标函数,直到一定的空闲时间过去后才执行,如果在这段时间内再次触发,则会重新计时。

  • 合并触发:多次连续触发的事件会被合并为一次触发,只有在最后一次触发后的空闲时间内没有再次触发才会执行目标函数。

应用:

  • 输入框实时搜索:在用户输入搜索关键词时,使用防抖可以避免在用户输入过程中频繁触发搜索请求,提高搜索性能和用户体验。

  • 按钮点击事件:在防止用户重复点击按钮后多次触发事件时,可以利用防抖来控制按钮的点击事件,确保只有在用户停止点击后才执行操作。

  • 窗口大小调整:当用户调整窗口大小时,很多页面布局或响应式设计需要重新计算和调整,使用防抖可以确保在用户停止调整窗口大小后再执行相关操作,避免频繁重绘页面。

  • 滚动事件:处理页面滚动事件时,可以利用防抖减少滚动过程中触发的频繁计算,只有在用户滚动停止后才执行相关处理。

优点:

  • 性能优化:防抖可以减少不必要的函数执行次数,降低性能消耗,提高页面性能和响应速度。

  • 用户体验:在一些交互场景下,使用防抖可以有效控制事件触发频率,提升用户体验和界面流畅度。

  • 数据请求控制:在需要向后端发起请求的场景中,防抖可以避免频繁请求数据,减少网络流量和服务器压力。

总的来说,防抖是一种常用的前端技术,通过控制事件触发频率,可以优化性能、提升用户体验,并有效应用于多种交互场景中。

案例:
逻辑梳理:

  • 在这个示例中,debounce函数接受两个参数:需要防抖的函数 func 和延迟时间 delay。返回一个新的函数,用于包裹原始函数 func,并在延迟时间内只允许触发一次执行。

  • 通过调用 debounce(doSomething, 1000) 创建一个防抖函数 debouncedFunction,然后可以多次调用 debouncedFunction,但实际执行的是 doSomething 函数,并且在延迟时间内多次调用会重新计时,只有在延迟时间后才会执行 doSomething 函数。

  • 这样可以确保在需要控制某个函数执行频率的场景下,防抖函数可以帮助避免频繁触发,提升性能和用户体验。

// 防抖函数
function debounce(func, delay) {
    let timeoutId;

    return function() {
        const context = this;
        const args = arguments;

        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
            func.apply(context, args);
        }, delay);
    };
}

// 示例:模拟一个需要防抖的函数
function doSomething() {
    console.log('Doing something important!');
}

// 创建防抖函数
const debouncedFunction = debounce(doSomething, 1000); // 设置延迟时间为1000毫秒

// 调用防抖函数
debouncedFunction(); // 第一次调用
debouncedFunction(); // 在1000毫秒内再次调用
debouncedFunction(); // 在1000毫秒内再次调用

二、什么是节流

定义: 与防抖类似,用于控制某个函数在短时间内被频繁调用的情况
特点:

  • 控制执行频率:节流会确保目标函数在一定时间间隔内只执行一次,即使触发事件多次也不会立即执行,而是在规定的时间间隔后执行。

  • 固定时间间隔:与防抖不同,节流在规定的时间间隔内始终按照固定频率执行函数,不会重新计时。

应用:

  • 页面滚动事件:在处理页面滚动事件时,通过节流可以限制触发频率,比如实现图片懒加载或滚动加载数据。

  • 窗口调整事件:当用户调整窗口大小时,使用节流可以确保在一定时间间隔内执行相关布局调整或响应式设计操作。

  • 按钮点击事件:类似于防抖,节流也可用于控制按钮点击事件的触发频率,确保在一定时间间隔内只执行一次。

  • 用户输入事件:在需要实时反馈用户输入内容的场景中,通过节流可以控制输入事件的处理频率,避免频繁更新UI。

优点:

  • 性能优化:节流可以有效减少函数执行次数,降低性能开销,提高页面性能和响应速度。

  • 流畅交互:通过控制事件执行频率,节流可以确保用户交互过程更加流畅,避免过多的重复操作。

  • 数据请求控制:在需要向服务器请求数据的场景中,节流可以限制请求频率,减少不必要的数据请求,节省网络流量和服务器资源。

总的来说,节流是一种常用的前端技术,通过控制函数执行频率,可以优化性能、提升用户体验,并广泛应用于各种交互场景中。

案例:
逻辑梳理:

  • 在这个示例中,throttle函数接受两个参数:需要节流的函数 func 和间隔时间 delay。返回一个新的函数,用于包裹原始函数 func,并在间隔时间内限制函数的执行频率。

  • 通过调用 throttle(doSomething, 1000) 创建一个节流函数 throttledFunction,然后可以多次调用 throttledFunction,但实际执行的是 doSomething 函数,并且保证在间隔时间内只执行一次。

  • 这样可以确保在需要控制某个函数执行频率的场景下,节流函数可以帮助限制函数的执行频率,避免过多的函数调用,提升性能和用户体验。

// 节流函数
function throttle(func, delay) {
    let lastCall = 0;

    return function() {
        const now = new Date().getTime();
        if (now - lastCall >= delay) {
            func();
            lastCall = now;
        }
    };
}

// 示例:模拟一个需要节流的函数
function doSomething() {
    console.log('Doing something important!');
}

// 创建节流函数
const throttledFunction = throttle(doSomething, 1000); // 设置间隔时间为1000毫秒

// 模拟连续调用节流函数
throttledFunction(); // 第一次调用
throttledFunction(); // 在1000毫秒内再次调用,不会执行
setTimeout(throttledFunction, 500); // 在500毫秒后调用,不会执行
setTimeout(throttledFunction, 1500); // 在1500毫秒后调用,会执行

防抖和节流

function debounce(func, wait, opts = {}) {
    let maxWait;
    if ('maxWait' in opts) {
        maxWait = opts.maxWait;
    }
    let leading = true; // 是否在第一次触发时立即执行
    let trailing = true; // 是否在最后一次触发后执行
    let lastCallTime; // 上次调用函数的时间
    let timeout; // 定时器
    let lastThis; // 函数执行时的 this
    let lastArgs; // 函数执行时的参数
    let lastInvokeTime; // 上次函数实际被调用的时间

    // 判断是否应该调用函数
    let shouldInvoke = function (now) {
        let sinceLastTime = now - lastCallTime;
        let sinceLastInvoke = now - lastInvokeTime;
        return lastCallTime === undefined || sinceLastTime > wait || sinceLastInvoke >= maxWait;
    }

    // 执行函数
    let invokeFunc = function (time) {
        lastInvokeTime = time; // 记录函数实际被调用的时间
        func.apply(lastThis, lastArgs); // 执行函数
    }

    // 启动定时器
    let startTimer = function (timerExpired, wait) {
        timeout = setTimeout(timerExpired, wait);
    }

    // 计算剩余等待时间
    let remainingWait = function (now) {
        return wait - (now - lastCallTime);
    }

    // 最后一次触发后执行函数
    let trailingEdge = function (time) {
        timeout = undefined;
        if (trailing) {
            invokeFunc(time);
        }
    }

    // 定时器到期后的处理逻辑
    let timerExpired = function () {
        let now = Date.now();
        if (shouldInvoke(now)) {
            return trailingEdge(now);
        }
        startTimer(timerExpired, remainingWait(now));
    }

    // 第一次触发时执行函数
    let leadingEdge = function (time) {
        lastInvokeTime = time;
        if (leading) {
            invokeFunc(time);
        }
        startTimer(timerExpired, wait); // 开启定时器,准备执行下一次函数调用
    }

    // 返回一个处理防抖逻辑的函数
    let debounced = function (...args) {
        lastThis = this;
        lastArgs = args;
        let now = Date.now();
        let isInvoking = shouldInvoke(now);
        lastCallTime = now;
        if (isInvoking) {
            if (timeout === undefined) {
                leadingEdge(now);
            }
        }
    }

    return debounced;
}
上一篇:cleanmyMac有必要吗,什么软件可以替代clean my mac