关于前端:自定义防抖函数五步应对复杂需求

54次阅读

共计 5017 个字符,预计需要花费 13 分钟才能阅读完成。

防抖定义

某些频繁操作的事件会影响性能,” 防抖 ” 能够用来缩小事件的响应频次,当事件触发的时候,绝对应的函数并不会立刻触发,而是会进行期待,只有期待了一段时间之后,事件进行触发,此时才会执行响应函数。

防抖案例

比方屏幕设定了 1 分钟的熄屏工夫,如果用户在这 1 分钟之内,没有对电脑进行任何的操作,那么电脑就会在一分钟时准时熄屏,但如果用户在第 50 秒的时候,挪动了一下鼠标,熄屏的工夫会从这一刻开始从新计时,往后计算满 1 分钟没有进行任何操作时才会持续熄屏。

防抖应用场景

在程序设计的过程中,很多场景都可能用到 ” 防抖 ”。

  • 输入框频繁输出、搜寻
  • 按钮频繁点击、提交信息,触发事件
  • 监听浏览器的滚动事件
  • 监听浏览器的缩放事件

没有应用防抖时

这里模仿一个商品搜寻框,咱们须要对用户输出的内容调用接口进行关联查问,来给用户进行搜寻提醒。
当没有应用防抖时,咱们会间接将函数绑定到对应的事件上。

// html 代码
<input>

// js 代码
const inputEl = document.querySelector('input')

let count = 0
const inputChange = function(){console.log(` 第 ${++count}次调用 inputChange 办法 `)
}
inputEl.oninput = inputChange

input 框内输出 ”javascript”,一共 10 个字符,所以办法调用了 10 次

这样的形式性能很低,其一是因为每输出一个字符就调用接口,对服务器造成的压力很大,其二是因为直到第 10 个字符,用户才进行输出,此时才获取了用户输出的残缺信息,前 9 个字符的调用就不是那么必要,那么,” 防抖 ” 的作用就是等到用户进行输出的时候,才去执行函数,防止了屡次执行造成的资源节约。

自定防抖函数

防抖函数实现的原理是,传入要执行的函数,以及须要期待推延的工夫,在通过一系列的解决之后,再去执行传入的函数。

第一步:根本版防抖实现

定义 setTimeout 推延函数的执行工夫,clearTimeout 用于在输出下一个字符还没有超过 1 秒钟时革除计时器,创立新的计时器,如果没有革除的话,那么相当于每个字符都会隔 1s 调用办法。

const debounce = function (fn, delay) {
  let timer = null;
  const _debounce = function () {if (timer) clearTimeout(timer);
    timer = setTimeout(()=>{fn()
    }, delay);
  };
  return _debounce;
};
inputEl.oninput = debounce(inputChange, 1000);

咱们是心愿达到当用户输出实现时,再执行函数,也就是输出完 10 个字符,才调用一次

但此时发现,event.target.value 用于获取用户输出的值变成了 undefined

第二步:拓展 this 和参数

在上一步定义函数时,this 对象和 event 参数都被失落了,在这里要把他们找回来,只须要在执行 fn 函数时,通过 call/apply/bind 来扭转 this 的执行,以及传入参数即可。

const debounce = function (fn, delay) {
  let timer = null;
  const _debounce = function (...args) {if (timer) clearTimeout(timer);
    timer = setTimeout(()=>{fn.apply(this,args)
    }, delay);
  };
  return _debounce;
};
inputEl.oninput = debounce(inputChange, 1000);

这样之后,咱们就能拿到用户的输出内容啦~

到这里为止,曾经实现了防抖的大部分应用场景,上面来看看更简单的需要吧~

第三步:函数立刻执行

在咱们下面定义的防抖函数中,是没有立刻执行的,也就是在输出第一个字符 ”j” 的时候,是不会调用函数,可能存在一些场景,等到用户输出实现再调用显得响应比拟迟缓,须要在第一个字符输出时就进行一次调用。
这里能够对于传入的函数减少一个参数,示意是否须要立刻执行,默认不须要,为 false,函数里在应用一个变量来保留是否须要首次执行,当首次执行实现后,再将这个变量置为 false

 const debounce = function (fn, delay, isImmediate = false) {
  let timer = null;
  let isExcute = isImmediate;
  const _debounce = function (...args) {if (isExcute) {fn.apply(this, args);
      isExcute = false;
    }
    if (timer) {clearTimeout(timer);
    }
    timer = setTimeout(() => {fn.apply(this, args);
      isExcute = isImmediate;
    }, delay);
  };
  return _debounce;
};

inputEl.oninput = debounce(inputChange, 1000, true);

输出字符 ”j” 时立刻调用以及当用户输出实现时进行一次调用,所以这里会有两次调用。

第四步:勾销性能

认真一看,咱们的防抖函数不可能勾销呀,只有到了工夫就肯定会执行,万一当用户输完内容之后,还没有到 1 秒钟,然而他曾经关掉窗口了呢,思考到这种状况,咱们得把勾销性能安顿上!
函数也是一个对象,咱们能够给函数对象上再绑定一个函数,在 return 的_debounce 上绑定一个 cancel 办法,当须要勾销的时候执行 cancel 办法

// html
<button> 勾销 </button>

// javascript
const cancelBtn = document.querySelector("button");
const debounce = function (fn, delay, isImmediate = false) {
  let timer = null;
  let isExcute = isImmediate;
  const _debounce = function (...args) {if (isExcute) {fn.apply(this, args);
      isExcute = false;
    }
    if (timer) {clearTimeout(timer);
    }
    timer = setTimeout(() => {fn.apply(this, args);
      isExcute = isImmediate;
    }, delay);
  };
  _debounce.cancel = function () {if (timer) {clearTimeout(timer);
    }
  };
  return _debounce;
};
const debounceFn = debounce(inputChange, 2000, true);
inputEl.oninput = debounceFn;
cancelBtn.onclick = debounceFn.cancel;

当输出了内容之后,在进行输出到点击按钮中没有超过定义的工夫(下面定义的是 2 秒钟),这样就只会执行首次输出的函数,在用户输出实现的那一次就不会执行了。

第五步:函数返回值

下面定义的 ” 防抖 ” 函数是没有返回值的,如果说在执行实现之后,心愿失去执行的后果,那么也有两种形式能够获取到
回调函数
在防抖函数的入参中减少第四个参数,是一个函数,用来获取防抖函数执行实现的后果

let count = 0;
const inputChange = function (event) {console.log(` 第 ${++count}次调用, 输出的内容为:${event?.target?.value}`);
  return 'hello world'
};
const debounce = function (fn, delay, isImmediate = false, callbackFn) {
  let timer = null;
  let isExcute = isImmediate;
  const _debounce = function (...args) {if (isExcute) {const result = fn.apply(this, args);
      callbackFn(result)
      isExcute = false;
    }
    if (timer) {clearTimeout(timer);
    }
    timer = setTimeout(() => {const result = fn.apply(this, args);
      callbackFn(result)
      isExcute = isImmediate;
    }, delay);
  };
  return _debounce;
};
const debounceFn = debounce(inputChange, 2000, true, function(result){console.log('获取执行的后果',result)
});
inputEl.oninput = debounceFn;

每执行一次防抖函数,都会执行回调函数里的内容。

promise
防抖函数的返回函数中,通过 promise 来返回胜利或失败的后果,以下代码只判断了胜利的执行条件,还能够加上失败的解决。
通过 promise 包裹的异步函数要通过调用能力获取响应后果,所以将防抖函数放在新函数中,将新函数作为 oninput 事件响应的函数。

const inputEl = document.querySelector("input");
  let count = 0;
  const inputChange = function (event) {
    console.log(` 第 ${++count}次调用, 输出的内容为:${event?.target?.value}`
    );
    return "hello world";
  };
  const debounce = function (fn, delay, isImmediate = false) {
    let timer = null;
    let isExcute = isImmediate;
    const _debounce = function (...args) {return new Promise((resolve, reject) => {if (isExcute) {const result = fn.apply(this, args);
          resolve(result);
          isExcute = false;
        }
        if (timer) {clearTimeout(timer);
        }
        timer = setTimeout(() => {const result = fn.apply(this, args);
          resolve(result);
          isExcute = isImmediate;
        }, delay);
      });
    };
    // 封装勾销性能
    _debounce.cancel = function () {if (timer) clearTimeout(timer);
      timer = null;
      isInvoke = false;
    };
    return _debounce;
  };
  const debounceFn = debounce(inputChange, 2000, true);
  const promiseCallback = function (...args) {debounceFn.apply(inputEl, args).then((res) => {console.log("promise 的执行后果", res);
    });
  };
  inputEl.oninput = promiseCallback;

和传入回调函数的模式统一,都是通过每执行一次防抖函数,就会返回一次后果。

在开发中应用防抖函数优化我的项目的性能,能够按如上形式自定义,也能够应用第三方库。

以上就是防抖函数相干内容,对于 js 高级,还有很多须要开发者把握的中央,能够看看我写的其余博文,继续更新中~

正文完
 0