防抖、节流【e.g:无限滚动】

484 阅读2分钟

lodash 提供了throttle节流API、debounce 防抖API

理解源码前,回顾JavaScript闭包原理:

let a = 1;
function f(){
	let a = 0;
	return function (b) {
		console.log(a,b);
		return a++ + b++;
	}
}

const res = f();
console.log(res(a)); // 0+1
console.log(res(a)); // 1+1
console.log(res(a)); // 2+1
const res1 = f();
console.log(res1(a)); // 0+1
console.log(res1(a)); // 1+1
console.log(res1(a)); // 2+1

函数防抖

多次接近的(相隔不超过时间设置)操作合并为一次操作进行,只在最后一次事件触发一次函数;或者第一次触发,下一次调用必须与前一次调用的时间间隔大于wait才会触发

适用于

  • 搜索问题,希望用户输入完最后一个字才调用查询接口
  • 用户点star的时候,希望用户点第一下的时候就去调用接口,且成功之后改变star按钮的样子

实现:维护一个定时器,返回一个推迟执行的函数

//防抖debounce 
function debounce(fn) {
    let timer = null; //闭包:维护一个计时器,创建一个标记存放定时器的返回值
    return function () {
        // 规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。
        clearTimeout(timer); 
        // 然后又创建一个新定时
        timer = setTimeout(() => {
            fn.apply(this, arguments);
        }, 500000000);
    };
}
// 处理函数
function handle() {
   console.log("处理中");
}
// 滚动事件
window.addEventListener('scroll', debounce(handle));

立即执行版本:

function getFixedLength(pre, len){
    let tempArray = pre.toString().split('');
    let addLen = len - tempArray.length;
    while(addLen--){
        tempArray.unshift(0);
    }
    return tempArray.join('');
}

// 用来获取当前时间戳的
function getCurrentTime() {
    const now = new Date();
    const min = getFixedLength(now.getMinutes(), 2);
    const sec = getFixedLength(now.getMilliseconds(), 3);
    const timeString = min + sec;
    return Number(timeString);
}

function debounce(fn, wait = 50) {
    let start = null; //闭包:维护一个计时器,创建一个标记存放定时器的返回
    let now = null;
    return function () {
        const now = getCurrentTime();
        const isTime = start ? (now - start >= wait? true : false): true;
        if(isTime){
            fn();
            start = getCurrentTime();
        }
        console.log('now',now,'start',start,'is',isTime);
    };
}

// 处理函数
function handle() {
    console.log("pop");
}

// 滚动事件
window.addEventListener('scroll', debounce(handle, 500));

函数节流

规定时间内只触发一次函数。原理是通过判断是否有延迟调用函数未执行

页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。适合用节流技术来实现。

//节流throttle 
function throttle(fn) {
    let isDoing= false; // 通过闭包保存一个标记
    return function () {
         // 在函数开头判断标记是否为true,不为true则return
        if (isDoing) return;
         //  设置为false,维护的时间起点
        isDoing = true;
        setTimeout(() => { 
            fn.apply(this, arguments);
            isDoing= false;//执行完毕,时间终点
        }, 1000);
    };
}

function http(e) {
   //
}
window.addEventListener('resize', throttle(http));

requestAnimationFrame浏览器原理节流

requestAnimationFrame使用一个回调函数作为参数。这个回调函数会在浏览器重绘之前调用。

window.addEventListener('scroll', () =>{
    window.requestAnimationFrame(handle); 
    }
);

无限滚动:滚动+分页器

思考:向上翻看消息记录怎么实现?

局部加载DOM