函数防抖

174 阅读3分钟

之前觉得已经掌握了函数防抖,今天看了一些大佬的文章,对防抖又有了新的了解,觉得自己之前掌握的只是一些皮毛,今天特此记录一下.

什么是防抖呢?根据个人的理解就是在用户某个操作下,高频触发了要执行的同一个函数,例如页面滚动,鼠标移入移出,快速点击按钮等触发函数事件。如果只是普通的函数方法还好,如果是函数中包含了ajax请求或者对dom的操作,都会加重浏览器负担,影响用户体验,所以函数防抖应运而生。所谓防抖,就是在函数高频触发下只执行一次,立即执行还是最后执行看网站需求。

非立即执行情况

举个最简单的例子,页面中有个按钮,点击后执行某个事件。

    <input type="button" class="btn" value="点击">
    
    var btn=document.querySelector(".btn");

    //要执行的方法
    function fun() {
        console.log("方法执行了!");
    }
    

基础版本:

    btn.onclick=debounce(fun,500);
    
   function debounce(fn, delay) {
        var timer;
        return function () {
            clearTimeout(timer) ;
            timer=setTimeout(()=>{
                fn();
            },delay)
        }
    }

通过js闭包的特性,将每次执行都要判断的timer保存下来,每次给timer赋值settimeout前先将定时器清除,在停止点击后的delay秒后执行。

进阶版本:

基础版本已经实现了我们想要的功能。但是如果当fun方法中用到了this,使用基础版本的话,this为window,因为settimeout是window下的方法。所以我们需要把当前的this保存下来,通过call或者apply方法来执行fun方法改变this指向。

    function debounce(fn, delay) {
        var timer;
        return function () {
            var _this=this;
            //将当前的this指向保存下来
            clearTimeout(timer) ;
            timer=setTimeout(()=>{
                fn.apply(_this);
            },delay)
        }
    }

最终版本: JS的事件处理函数中会提供事件对象event,当fun方法需要event对象进行相关操作时,我们需要把这个参数传递过去。

    function debounce(fn, delay) {
        var timer;
        return function () {
            var _this = this;
            //将当前的this指向保存下来
            var arg=arguments;
            //保存arguments对象将参数传给fn
            clearTimeout(timer);
            timer = setTimeout(() => {
                fn.apply(_this,arg);
            }, delay)
        }
    }

arguments是函数内部的一个类数组对象,而apply方法的第二个参数刚好可以传一个数组参数。


立即执行情况

立即执行情况的代码主要变化为判断timer来决定是执行函数还是清除定时器方法,首次执行后timer为假,此时立即执行fun方法,也就是我们想要的立即执行效果,然后给timer赋值定时器,执行timer=null方法,在之后的高频触发下,timer一直存在,只有当停止触发delay时间后timer=null,此时下次触发又会立即执行,由于别的代码变化不大,这里我们直接上最终版本代码。

最终版本:

    function debounce(fn,delay){
        var timer;
        return function(){
            var _this = this;
            var arg = arguments;
            timer ? clearTimeout(timer) : fn.apply(_this,arg);
            timer=setTimeout(()=>{
                clearTimeout(timer);
                timer=null;
            },delay)
        }
    }

结语:

第一次写文章语言逻辑什么的有些乱,主要就是想记录一下自己的所想所得。既然提到了防抖,那就不得不提节流,下篇文章我会记录一下我对节流的一些见解。