函数防抖

85 阅读2分钟

一、简化版本

1、防抖函数

function debounce(fun, wait, immediate) {
    var timer = null
    
    return function proxy() {
        var self = this,
            params = [].slice.call(arguments),
            callNow = immediate && !timer
        if (timer) clearTimeout(timer)

        timer = setTimeout(function () {
            clearTimeout(timer)
            timer = null
            !callNow && handle.apply(self, params)
        }, wait)

        if (callNow) handle.apply(self, params)
    }
}

2、调用

var btn = document.getElementById('btn')
var handle = function () {
    var xhr = new XMLHttpRequest
    xhr.open('GET', '../code.txt')
    xhr.onreadystatechange = function () {
    }
    xhr.send(null)
    return 1111
}
btn.onclick = debounce(handle, 1000, true)

二、复杂版本

1、函数防抖定义

(1)设置一个WAIT时间,此时间是用户自己定义,多久内触发多次算是频繁触发

(2)函数防抖:在频繁触发的模式下,我们只识别一次「可以只识别第一次immediate=true,也可以识别最后一次」

2、在每个设置的时间的最后执行

我们只要设置一个定时器,然后wait时间之后执行这个需要执行的函数,如果在这个时间之内频繁的触发了这个函数,那么我们需要把之前的定时器干掉,重新设置定时器,直到是在我们设置的时间之内的最后执行函数即可(这个时间之内必须只执行最后一次,不能干扰);

3、在每个设置的时间的开始执行

我们只需要传入immediate这个字段,让其为true,这样的话,我们判读只要是timer是null的话,就可以让这个函数执行,然后timer设置为了一个定时器,这个定时器在wait秒之后变为null即可;

function debounce(func, wait, immediate) {
    if (typeof func !== "function") throw new TypeError('func must be a function!');
    if (typeof wait === "undefined") {
        wait = 500;
        immediate = false;
    }
    if (typeof wait === "boolean") {
        immediate = wait;
        wait = 500;
    }
    if (typeof wait !== "number") throw new TypeError('wait must be a number!');
    if (typeof immediate !== "boolean") throw new TypeError('immediate must be a boolean!');

    var timer = null
    return function proxy() {
        var self = this,
            params = [].slice.call(arguments),
            callNow = !timer && immediate;
        if (timer) clearTimeout(timer);
        timer = setTimeout(function () {
            // 清除最后一次定时器
            clearTimeout(timer);
            timer = null;
            // 符合执行的是最后一次「触发在结束边界」
            !immediate  && func.apply(self, params);
        }, wait);
        // 符合第一次立即执行「触发在开始的边界」
          callNow && func.apply(self, params);
    };
}

自己的分析:

为什么要使用闭包的机制呢 ? 因为绑定给事件的函数是一个立即执行函数,如果我们不返回一个函数,就会立即将这个函数执行,这样是不对的。

4、执行

    const handle = function handle(ev) {
        console.log(this, ev);
         setTimeout(() => {
            $.ajax({
                url: './code.txt',
                success(result) {
                    console.log(result);
                }
            });
        }, 1000); 
    };
    submit.onclick = debounce(handle, true);
    // 用户疯狂点击,proxy函数会疯狂的执行,但是我们最后要执行的是handle,所以我们只需要在proxy执行多次的时候,基于一些列的判断处理,让handle只执行一次即可