「我正在参与会员专属活动-源码共读第一期」

本篇文章介绍的是debounce防抖。

防抖很常见了,简直随处可见,例如你在写文章的时分,不停地在左面的编辑栏张狂的输出,右边的预览栏没有马上处理,仅仅在你时刻短中止的那一刻,将你的文字在右边显示出来。由于这里运用了防抖,作用是:避免重复无效的操作,只取最终一次有用操作。

用户交互这块运用防抖也有场景:

  • 用户拖拽浏览器边框
  • 键盘敲击
  • 鼠标移动

源码

这些操作也是很频繁的,没必要每次都处理,咱们只处理最终那一次就好了。

简版

function debounce(func, wait = 1000) {
      let timer;
      return function () {
        clearTimeout(timer);
        timer = setTimeout(func, wait);
      };
}

下图是直接调用和debounce调用的this和参数比较

只取最终一次的有用操作-防抖

问题:

  1. this指向过错
  2. 函数参数丢失
  3. 函数返回值取不到

处理this问题

获取到当前this,然后通过apply强制绑定this

function debounce(func, wait = 1000) {
      let timer;
      return function () {
        let context = this
        clearTimeout(timer);
        timer = setTimeout(function (){
            func.apply(context)
        },wait);
      };
}

如下图:现在this不一致的问题处理了

只取最终一次的有用操作-防抖

参数丢失问题

通过arguments这个函数内置对象获取函数的参数

function debounce(func, wait = 1000) {
      let timer;
      return function () {
        let context = this, args = arguments
        clearTimeout(timer);
        timer = setTimeout(function (){
            func.apply(context,args)
        },wait);
      };
}

如下图:现在函数的参数能接纳到了

只取最终一次的有用操作-防抖

函数返回值

这个好办在函数直接的时分通过一个变量接纳

  function debounce(func, wait = 1000) {
      let timer, result;
      return function () {
        let context = this,
          args = arguments;
        clearTimeout(timer);
        timer = setTimeout(function () {
          result = func.apply(context, args);
          console.log(result);
        }, wait);
      };
   }

当即履行

如果想当即履行,不想等时刻到了之后履行,需要参加一个是否当即履行的标志

 function debounce(func, wait = 1000, immediate = false) {
      let timer, result;
      return function () {
        let context = this,
          args = arguments;
        clearTimeout(timer);
        if(immediate){
            var callnow = !timer // 避免重复履行
            timer = setTimeout(()=>{
               timer = null
            },wait) //timer推迟置为null 函数直接履行
            if(callnow) result = func.apply(context, args);
        }else{
           // 不然就推迟履行
            timer = setTimeout(function () {
              result = func.apply(context, args);
            }, wait);
        }
      };
   }

重置debounce

想在推迟的过程中取消颤动,重新开启一个。

首先将返回的函数保存到debounce,然后添加一个cancel特点用于重置变量。

function debounce(func, wait = 1000, immediate = false) {
      let timer, result;
      var debounce = function () {
        let context = this,
          args = arguments;
        clearTimeout(timer);
        if(immediate){
            var callnow = !timer // 避免重复履行
            timer = setTimeout(()=>{
               timer = null
            },wait) //timer推迟置为null 函数直接履行
            if(callnow) result = func.apply(context, args);
        }else{
           // 不然就推迟履行
            timer = setTimeout(function () {
              result = func.apply(context, args);
            }, wait);
        }
      };
      debounce.cancel = function(){
          clearTimeout(timer);
          timer = result = null
      }
      return debounce
   }

结束语

在运用函数作为参数的时分,要首先保证包裹后的函数履行和未包裹前的函数的履行效果是相同的。也就是说在对函数做一些功能加强的时分,不能改动函数本来的逻辑。wrapped(fn)

避免颤动在运用的场景十分广泛,有时分还得留意浏览器的兼容和setTimeout时刻不准确的问题。