手写防抖(debounce)和节流(throttle)

本文详细介绍了如何使用手写实现的防抖(debounce)和节流(throttle)函数,包括参数验证、this指向调整、立即执行、取消功能以及定时器实现。通过实例展示了如何在输入框事件中应用这些技巧,同时提供了取消操作的灵活性。
摘要由CSDN通过智能技术生成

目录

测试代码

防抖(debounce)

基本功能实现

添加参数及this指向功能

第一次立即执行功能

取消功能

节流(throttle)

基本功能实现

增加this执向

最后一次在禁止时间段内是否执行

添加取消按钮

定时器实现节流


测试代码

import debounce from './手写防抖.js';
import throttle from './手写节流.js';

const inputEl = document.querySelector("input");
const cancelBtn = document.querySelector("#cancel");

let onBody = function (event) {
    console.log('最新的内容为: ', event, this);
}

// 防抖测试代码 ---------------
let debounceChange = debounce(onBody, 3000);
inputEl.oninput = debounceChange;

cancelBtn.onclick = function () {
    debounceChange.cancel()
}

// 节流测试代码 ---------------
let throttleChange = throttle(onBody, 3000, true);
inputEl.oninput = throttleChange;

cancelBtn.onclick = function () {
    throttleChange.cancel()
}

防抖(debounce)

需求:使函数延迟X秒后执行,未执行前调用则重置延迟时间。

实现思路:每次触发清楚宏任务中的事件并重新添加定时器执行。

基本功能实现

function debounce1(fn, delay = 0) {
    let timer = null;
    // 参数验证
    try {
        if (typeof fn !== 'function') throw new Error('参数一不是一个函数!');
        if (typeof delay !== 'number' || delay < 0) throw new Error('参数二不是一个正数!');
    } catch (err) {
        console.log(err);
    }

    return function _debounce() {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn()
        }, delay);
    }
}

添加参数及this指向功能

function debounce2(fn, delay = 0) {
    let timer = null;
    // 参数验证
    try {
        if (typeof fn !== 'function') throw new Error('参数一不是一个函数!');
        if (typeof delay !== 'number' || delay < 0) throw new Error('参数二不是一个正数!');
    } catch (err) {
        console.log(err);
    }

    // 这里的args是事件回调的参数,和定义方法时传递的不冲突
    return function _debounce(...args) {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            fn.apply(this, args) //改变this指向并把onclick的event传递
        }, delay);
    }
}

第一次立即执行功能

function debounce3(fn, delay = 0, immediate = false) {
    let timer = null;
    let isInvoke = false
    // 参数验证
    try {
        if (typeof fn !== 'function') throw new Error('参数一不是一个函数!');
        if (typeof delay !== 'number' || delay < 0) throw new Error('参数二不是一个正数!');
        if (typeof immediate !== 'boolean') throw new Error('参数三未不是一个布尔值!');
    } catch (err) {
        console.log(err);
    }

    return function _debounce(...args) {
        if (timer) clearTimeout(timer)
        if (!isInvoke && immediate) {
            fn.apply(this, args) //改变this指向并把onclick的event传递
            isInvoke = true
        } else {
            timer = setTimeout(() => {
                fn.apply(this, args) //改变this指向并把onclick的event传递
                isInvoke = false
            }, delay);
        }
    }
}

取消功能

/**
 * 
 * @param {节流方法} fn 
 * @param {延迟时间} delay 
 * @param {是否立即执行} immediate 
 * @returns 
 */
export default function debounce(fn, delay = 0, immediate = false) {
    let timer = null;
    let isInvoke = false
    // 参数验证
    try {
        if (typeof fn !== 'function') throw new Error('参数一不是一个函数!');
        if (typeof delay !== 'number' || delay < 0) throw new Error('参数二不是一个正数!');
        if (typeof immediate !== 'boolean') throw new Error('参数三未不是一个布尔值!');
    } catch (err) {
        console.log(err);
    }

    const _debounce = function (...args) {
        if (timer) clearTimeout(timer)
        if (!isInvoke && immediate) {
            fn.apply(this, args) //改变this指向并把onclick的event传递
            isInvoke = true
        } else {
            timer = setTimeout(() => {
                fn.apply(this, args) //改变this指向并把onclick的event传递
                isInvoke = false
            }, delay);
        }
    }
    // 添加取消函数
    _debounce.cancel = () => {
        if (timer) clearTimeout(timer)
        timer = null
        isInvoke = false
    }

    return _debounce
}

节流(throttle)

需求:X秒内只能执行一次

实现思路:

对比调用时间及上次执行时间的差决定是否执行(时间戳)

每次执行设置定时器,X秒后才能再次执行当前函数。(定时器)

基本功能实现

function throttle1(fn, delay = 0) {
    let lastTime = 0
    // 参数验证
    try {
        if (typeof fn !== 'function') throw new Error('参数一不是一个函数!');
        if (typeof delay !== 'number' || delay < 0) throw new Error('参数二不是一个正数!');
    } catch (err) {
        console.log(err);
    }

    const _throttle = function () {
        // 当前时间
        let nowTime = new Date().getTime();

        // 设置间隔时间 - ( 当前时间 - 上一次执行时间 )
        const remainTime = delay - (nowTime - lastTime)

        if (remainTime <= 0) {
            fn()
            lastTime = nowTime
        }
    }
    return _throttle
}

增加this执向

function throttle2(fn, delay = 0) {
    let lastTime = 0
    // 参数验证
    try {
        if (typeof fn !== 'function') throw new Error('参数一不是一个函数!');
        if (typeof delay !== 'number' || delay < 0) throw new Error('参数二不是一个正数!');
    } catch (err) {
        console.log(err);
    }

    const _throttle = function (...args) {
        // 当前时间
        let nowTime = new Date().getTime();

        // 设置间隔时间 - ( 当前时间 - 上一次执行时间 )
        const remainTime = delay - (nowTime - lastTime)

        if (remainTime <= 0) {
            fn.apply(this, args)
            lastTime = nowTime
        }
    }
    return _throttle
}

最后一次在禁止时间段内是否执行

function throttle3(fn, delay = 0, trailing = false) {
    let lastTime = 0
    let timer = null
    // 参数验证
    try {
        if (typeof fn !== 'function') throw new Error('参数一不是一个函数!');
        if (typeof delay !== 'number' || delay < 0) throw new Error('参数二不是一个正数!');
        if (typeof trailing !== 'boolean') throw new Error('参数三不是一个布尔值!');
    } catch (err) {
        console.log(err);
    }

    const _throttle = function (...args) {
        // 当前时间
        let nowTime = new Date().getTime();

        // 设置间隔时间 - ( 当前时间 - 上一次执行时间 )
        const remainTime = delay - (nowTime - lastTime)

        if (remainTime <= 0) {
            fn.apply(this, args)
            lastTime = nowTime
            clearTimeout(timer)
        } else {
            if (!trailing) return
            if (timer) clearTimeout(timer);
            timer = setTimeout(() => {
                fn.apply(this, args)
                lastTime = nowTime
            }, remainTime);
        }
    }
    return _throttle
}

添加取消按钮

/**
 * 
 * @param {节流函数} fn 
 * @param {延迟时间} delay 
 * @param {最后一次是否执行} trailing 
 * @returns 
 */
function throttle(fn, delay = 0, trailing = false) {
    let lastTime = 0
    let timer = null
    // 参数验证
    try {
        if (typeof fn !== 'function') throw new Error('参数一不是一个函数!');
        if (typeof delay !== 'number' || delay < 0) throw new Error('参数二不是一个正数!');
        if (typeof trailing !== 'boolean') throw new Error('参数三不是一个布尔值!');
    } catch (err) {
        console.log(err);
    }

    const _throttle = function (...args) {
        // 当前时间
        let nowTime = new Date().getTime();

        // 设置间隔时间 - ( 当前时间 - 上一次执行时间 )
        const remainTime = delay - (nowTime - lastTime)

        if (remainTime <= 0) {
            fn.apply(this, args)
            lastTime = nowTime
            clearTimeout(timer)
        } else {
            if (!trailing) return
            if (timer) clearTimeout(timer);
            timer = setTimeout(() => {
                fn.apply(this, args)
                lastTime = nowTime
            }, remainTime);
        }
    }

    _throttle.cancel = function () {
        if (timer) clearTimeout(timer);
        timer = null
        lastTime = 0
    }

    return _throttle
}

定时器实现节流

/**
 * 
 * @param {节流函数} fn 
 * @param {延迟时间} delay 
 * @param {最后一次是否执行} trailing 
 * @returns 
 */
function throttle(fn, delay = 0, trailing = false) {
    let isInvoke = false
    let isLastInvoke = false
    let timer = null
    // 参数验证
    try {
        if (typeof fn !== 'function') throw new Error('参数一不是一个函数!');
        if (typeof delay !== 'number' || delay < 0) throw new Error('参数二不是一个正数!');
        if (typeof trailing !== 'boolean') throw new Error('参数三不是一个布尔值!');
    } catch (err) {
        console.log(err);
    }

    const _throttle = function (...args) {
        let _this = this
        function t() {
            timer = setTimeout(() => {
                if (isLastInvoke) {
                    isLastInvoke = false
                    fn.apply(_this, args)
                    t();
                } else {
                    isInvoke = false
                }
            }, delay);
        }

        if (!isInvoke) {
            isInvoke = true
            fn.apply(this, args)
            t();
        } else {
            if (!trailing) return
            isLastInvoke = true
        }
    }

    _throttle.cancel = function () {
        if (timer) clearTimeout(timer);
        timer = null
        isInvoke = false
        isLastInvoke = false  
    }

    return _throttle
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值