ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

防抖与节流

2022-01-01 16:34:58  阅读:163  来源: 互联网

标签:防抖 节流 timer func duration 函数


目录

防抖与节流

防抖

短时间内多次触发同一函数,只执行第一次/最后一次。比如说:一只小猫冷的发抖,稍用力按住它,它就不抖了。可以在它还没抖的时候按住,也可以在它抖了很久之后按住

普通防抖

/**
 * 普通实现
 * 函数防抖[函数高频率被调用只执行第一次/最后一次]
 * @param {Function} func 需要防抖的函数
 * @param {Number} [duration=1000] 防抖时长[多长时间内的点击需要防抖]
 */
function debounce (func, duration = 1000) {
    let timer = null;
    return function (...args) {
        /*if (timer!== null)*/ clearTimeout(timer);
        timer = setTimeout(() => {
            clearTimeout(timer);
        	func.apply(this, args);
        }, delay);
    }
}

如果要给防抖函数绑定this,使用方式如下

绑定this

function mClick() {
    console.log('我被点名啦!');
}

debounce(mClick).bind(this);

有配置项的防抖

/**
 * 函数防抖
 * @param {Function} func 需要防抖的函数
 * @param {Boolean} [first=true] 是否首次防抖
 * @param {Number} [duration=1000] 防抖持续时长
 * @returns 
 */
function debounce(func, first = true, duration = 1000){
    // 声明一个空的定时器id,为防抖做准备
	let timer = null;
	// 返回函数是因为这个防抖函数可能会被绑定/赋值给其他函数使用
	return function (...args) {
		first && !timer && func.apply(this, args);
		// 不用判断timer是否存在,传入一个错误的 ID 给 clearTimeout()不会有任何影响;也不会抛出异常。
		clearTimeout(timer);
		timer = setTimeout(() => {
            clearTimeout(timer);
			// 非首次防抖则调用这个函数
			!first && func.apply(this, args);
			// 为下一次防抖做准备
			timer = null;
		}, duration);
	};
}

可取消的防抖

/**
 * 函数防抖
 * @param {Function} func 需要防抖的函数
 * @param {Boolean} [first=true] 是否首次防抖
 * @param {Number} [duration=1000] 防抖持续时长
 * @returns 
 */
function debounce(func, first = true, duration = 1000){
    // 声明一个空的定时器id,为防抖做准备
	let timer = null;
    let debounced = function (...args) {
		first && !timer && func.apply(this, args);
		// 不用判断timer是否存在,传入一个错误的 ID 给 clearTimeout()不会有任何影响;也不会抛出异常。
		clearTimeout(timer);
		timer = setTimeout(() => {
            clearTimeout(timer);
			// 非首次防抖则调用这个函数
			!first && func.apply(this, args);
			// 为下一次防抖做准备
			timer = null;
		}, duration);
	};
    debounced.cancel = function () {
        clearTimeout(timer);
        // 为下一次防抖做准备
        timer = null;
    }
	// 返回函数是因为这个防抖函数可能会被绑定/赋值给其他函数使用
	return debounced;
}

节流

一段时间内只允许函数执行一次

使用场景:滚动条滚动,过程中速度可能很快,但是肉眼捕捉不到,所以那些肉眼捕捉不到的函数调用是不必要的,故而节流,节省性能。

时间戳节流

首次执行,duration期间内尾次不执行

会在第一次触发事件时立即执行[给事件绑定函数与真正触发事件的间隔一般大于duration],以后每过duration时长后,函数可以被再次触发执行。

/**
 * 函数节流
 * @param {Function} func 需要节流的函数
 * @param {Number} [duration=1000] 节流持续的时长
 */
function throttle(func, duration = 1000) {
    let prev = 0;
    return function (...args) {
        const now = Date.now();
        // 两次函数被调用的时长大于节流时长
        if (now - prev > duration) {
            func.apply(this, args);
            prev = Date.now();
        }
    }
}

绑定this

function mScroll () {
    console.log('我滑滑梯啦!');
}
throttle(mScroll).bind(this);

定时器节流

第一次触发不会执行,而是在duration时长后执行,当最后一次停止触发后,还会再执行一次函数。

/**
 * 函数节流
 * @param {Function} func 需要被节流的函数
 * @param {Number} [duration=1000] 节流持续时长
 */
function throttle(func, duration = 1000) {
    let timer = null;
    return function (...args) {
        // 定时器未执行结束,函数再次被调用不生效
        if (!timer) {
            timer = setTimeout(() => {
                clearTimeout(timer);
                func.apply(this, args);
                timer = null;
            }, duration);
        }
    }
}

时间戳 + 定时器的节流

首、尾次均执行。[两者交替使用,第一次是时间戳,第二次是定时器,第三次是时间戳,第四次是定时器……,理想状态下如此,但碍于定时器的消息队列处理等影响,有时可能不是这样。]

/**
 * 函数节流
 * @param {Function} func 需要被节流的函数
 * @param {Number} [duration=1000] 节流持续时长
 */
function throttle (func, duration = 1000) {
    let timer, prev = 0;
    return function (...args) {
        const now = Date.now();
        // 解决第一次不执行
        if (now - prev > duration) {
            // 某个定时器执行结束,清除定时器
            if (timer){
                clearTimeout(timer);
                // 如果定时器超过了duration时长仍然没有执行函数[被其他事情影响],则取消这个定时器,立即执行函数
           		timer = null;
            }
            func.apply(this, args);
            prev = Date.now();
        } else if (!timer) { // 解决最后一次不执行
            // 两次调用时长小于duration时使用
            timer = setTimeout(() => {
                prev = Date.now();
                func.apply(this, args);
                timer = null;
            }, duration);
        }
    };
}

有配置项的节流

/**
 * 函数节流
 * @param {Function} func 需要被节流的函数
 * @param {Object} option 节流配置项
 * option = { 
 * 		leading: false, // 禁止第一次执行
 * 		trailing: false // 禁止最后一次执行
 *	}
 *		leading和trailing不能同时为false[同为false,节流失效]
 * @param {Number} [duration=1000] 节流持续时长
 */
function throttle (func, option, duration = 1000) {
    !option && (option = { leading: true, trailing: true });
    let timer, prev = 0;
    return function (...args) {
        const now = Date.now();
        if (!(option.leading || prev)) {
            prev = now;
        }
        // 解决第一次不执行
        if (now - prev > duration) {
            // 某个定时器执行结束,清除定时器
            if (timer){
                clearTimeout(timer);
                // 如果定时器超过了duration时长仍然没有执行函数[被其他事情影响],则取消这个定时器,立即执行函数
           		timer = null;
            }
            func.apply(this, args);
            prev = Date.now();
        } else if (!timer && option.trailing) { // 解决最后一次不执行
            // 两次调用时长小于duration时使用
            timer = setTimeout(() => {
                clearTimeout(timer);
                prev = Date.now();
                func.apply(this, args);
                timer = null;
            }, duration);
        }
    };
}

可取消的节流

/**
 * 函数节流
 * @param {Function} func 需要被节流的函数
 * @param {Object} option 节流配置项
 * option = { 
 * 		leading: false, // 禁止第一次执行
 * 		trailing: false // 禁止最后一次执行
 *	}
 *		leading和trailing不能同时为false[同为false,节流失效]
 * @param {Number} [duration=1000] 节流持续时长
 */
function throttle (func, option, duration = 1000) {
    !option && (option = { leading: true, trailing: true });
    let timer, prev = 0;
    
    let trottled = function (...args) {
        const now = Date.now();
        if (!(option.leading || prev)) {
            prev = now;
        }
        // 解决第一次不执行
        if (now - prev > duration) {
            // 某个定时器执行结束,清除定时器
            if (timer){
                clearTimeout(timer);
                // 如果定时器超过了duration时长仍然没有执行函数[被其他事情影响],则取消这个定时器,立即执行函数
           		timer = null;
            }
            func.apply(this, args);
            prev = Date.now();
        } else if (!timer && option.trailing) { // 解决最后一次不执行
            // 两次调用时长小于duration时使用
            timer = setTimeout(() => {
                clearTimeout(timer);
                prev = Date.now();
                func.apply(this, args);
                timer = null;
            }, duration);
        }
    };
    
    trottled.cancel = function () {
        clearTimeout(timer);
        timer = null;
        prev = 0;
    }
    return trottled;
}

window.requestAnimationFrame节流

节流时机不可控[函数不知道什么时候就被调用了,因为不知道什么时间浏览器会重绘]

/**
 * 函数节流
 * @param {Function} func 需要节流的函数
 */
function throttle(func) {
    let lock = false;
    return function (...args) {
        !lock && window.requestAnimationFrame(() => {
            func.apply(this, args);
            lock = false;
        });
        lock = true;
    }
}

参考

JS系列之事件节流(Debounce)

[JavaScript] 节流(throttle)-防抖(debounce) 不懵圈指北

JS手写系列之防抖节流

JS事件节流(Throttle)

使用 requestAnimationFrame 替代 throttle 优化页面性能

标签:防抖,节流,timer,func,duration,函数
来源: https://www.cnblogs.com/zoexu/p/15755579.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有