最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 还有人不知道防抖节流?(二)

    正文概述 掘金(蒲月阿七)   2021-02-13   486

    第一篇介绍了防抖节流函数的原理和常见应用场景,接下来我们来看看具体实现。

    这次先介绍防抖函数。

    underscore.js中它的源码如下:

      _.debounce = function(func, wait, immediate) {
        var timeout, result;
    
        var later = function(context, args) {
          timeout = null;
          if (args) result = func.apply(context, args);
        };
    
        var debounced = restArguments(function(args) {
          if (timeout) clearTimeout(timeout);
          if (immediate) {
            var callNow = !timeout;
            timeout = setTimeout(later, wait);
            if (callNow) result = func.apply(this, args);
          } else {
            timeout = _.delay(later, wait, this, args);
          }
    
          return result;
        });
    
        debounced.cancel = function() {
          clearTimeout(timeout);
          timeout = null;
        };
    
        return debounced;
      };
    

    你会发现里面有一个函数我们还没了解它的实现,首先我们先来看看这个函数restArguments的源码。

    var restArguments = function(func, startIndex) {
      startIndex = startIndex == null ? func.length - 1 : +startIndex;
      return function() {
        var length = Math.max(arguments.length - startIndex, 0),
            rest = Array(length),
            index = 0;
        for (; index < length; index++) {
          rest[index] = arguments[index + startIndex];
        }
        switch (startIndex) {
          case 0: return func.call(this, rest);
          case 1: return func.call(this, arguments[0], rest);
          case 2: return func.call(this, arguments[0], arguments[1], rest);
        }
        var args = Array(startIndex + 1);
        for (index = 0; index < startIndex; index++) {
          args[index] = arguments[index];
        }
        args[startIndex] = rest;
        return func.apply(this, args);
      };
    };
    

    是不是觉得少了点什么?嗯,少了注释!

    先别着急,我们来看看这个函数的作用是什么。

    在ES6中,我们可以使用rest接受函数的剩余参数,并且以数组的方式呈现。

    function func(a, ...rest) {
        // ...
    }
    
    func(1, 2, 3, 4); 
    // 其中a参数值为1,rest参数为 [2, 3, 4]
    

    在underScore中,因为不确定执行的环境,underScore内部自己实现了一个restArguments方法,就是用es5的方式来实现,可以让我们可以在大多数浏览器环境下使用剩余参数语法。

    使用方法:

    function func(a, ...rest) {
        // ...
    }
    
    const resFun = _.restArguments(func);
    resFun(1, 2, 3, 4); 
    // 其中a参数值为1,rest参数为 [2, 3, 4]
    

    以下配上注释啦!

    function test(a, b) {
    	// ...
    }
    
    test(1,2,3,4)
    console.log(test.length) // 3
    
    var restArguments = function(func, startIndex) {
    
    // func.length是形参的个数,以上面的例子的话是2。
    // 已经有startIndex的话就直接用,没有的话就用func.len找到最后参数所在的位置
      startIndex = startIndex == null ? func.length - 1 : +startIndex;
      
      return function() {
      
      // arguments.length是实际参数的个数,以上面的例子的话是4
      // 有可能实际参数大于形式参数,所以arguments.length - startIndex可能小于0,下面取最大值是防止出现负值的情况
        var length = Math.max(arguments.length - startIndex, 0),
        
            rest = Array(length),
            index = 0;
            
        // 循环拿到剩余参数数组
        for (; index < length; index++) {
          rest[index] = arguments[index + startIndex];
        }
        
        // 接下来要把参数传递给func,也就是执行该函数原本的功能
       
       // 使用call方法实现
        switch (startIndex) {
          // 剩余参数前面没有参数
          case 0: return func.call(this, rest);
          // 剩余参数前面有一个参数
          case 1: return func.call(this, arguments[0], rest);
          // 剩余参数前面有两个参数
          case 2: return func.call(this, arguments[0], arguments[1], rest);
        }
        
        // 你会发现上面使用call方法的时候如果剩余参数前面剩余的参数很多,是没办法一个一个传递的!
        
        // 使用apply方法实现,需要把剩余参数数组和剩余参数前面的参数放在一个数组里面。
        var args = Array(startIndex + 1);
        for (index = 0; index < startIndex; index++) {
          args[index] = arguments[index];
        }
        args[startIndex] = rest;
        return func.apply(this, args);
      };
    };
    
    // 最后!你一定发现了,竟然可以用apply解决,干嘛还要多此一举写多个call方式?
    // 其实,这里做了一个性能优化,因为call的性能比apply的高。
    

    OK,从上面我们可以了解到restArguments函数,给它传递一个函数以及一个多余参数开始索引(startIndex)作为参数,它会返回一个函数,我们在调用返回的函数时,开始索引之后的多余参数会被放入到数组中,然后一并传递给restArgs的第一个参数函数调用(作为最后一个参数)。

    有时候我们所写的函数不确定有多少个要传递的参数,这样在函数内部实现参数处理时就会比较棘手。这时候使用这个函数进行处理之后,可以把这些剩余的参数组合成数组添加到第一个参数的尾部。

    接下来我们来具体看看debounce函数吧!

      // 返回一个定时器延迟函数,这个看看就可以知道了吧!
      _.delay = restArguments(function(func, wait, args) {
        return setTimeout(function() {
          return func.apply(null, args);
        }, wait);
      });
    
      _.debounce = function(func, wait, immediate) {
        // 定义定时器和返回结果
        var timeout, result;
        
        // 清空定时器,执行函数
        var later = function(context, args) {
          timeout = null;
          if (args) result = func.apply(context, args);
        };
        
        // 关键实现
        var debounced = restArguments(function(args) {
        
        // 如果已经有定时器了,先清空
          if (timeout) clearTimeout(timeout);
          
          if (immediate) {	// 如果立即执行参数是true,表明先立刻执行一次
          
          	// 定义callNow,根据它判断要不要直接执行一次,跟timeout定时器绑定起来,是为了避免在第一次立即执行之后,已经有定时器了,但函数会一直触发执行。
            var callNow = !timeout;
            
            // 设置定时器延迟指定时间执行
            timeout = setTimeout(later, wait);
            
            // 如果需要先立即执行一次,现在是第一次触发,那么立即执行函数一次
            if (callNow) result = func.apply(this, args); 
            
          } else {	// 如果立即执行参数是false
          	// 重置定时器,延迟指定时间执行
            timeout = _.delay(later, wait, this, args);
          }
    	
        // 如果func函数本身是有返回值的,也要返回去
          return result;
        });
    	
        // 如果我的wait延迟时间很长!我等不及想取消这个函数的执行!这时候就可以调用cancel取消掉啦
        debounced.cancel = function() {
          clearTimeout(timeout);	// 清空定时器
          timeout = null;	// 防止内存泄漏
        };
        
        // 返回处理后的函数对象
        return debounced;
      };
    

    嗯,明天再更throttle函数~


    起源地下载网 » 还有人不知道防抖节流?(二)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元