函数节流和防抖

函数的高阶使用2

我们来看一个分析:
如果要实现一个拖拽功能,需要一路监听 mousemove 事件,在回调中获取元素当前位置,然后重置dom的位置来进行样式改变。如果不加以控制,每移动一定像素而触发的回调数量非常惊人,回调中又伴随着 DOM 操作,继而引发浏览器的重排与重绘,性能差的浏览器可能就会直接假死.

在某些情况下会引发函数被非常频繁地调用,而造成大的性能问题。解决性能问题的处理办法就是函数节流和函数防抖。

函数防抖

是函数在特定的时间内不被再调用后执行。 也就是让某个函数在上一次执行后,满足等待某个时间内不再触发此函数后再执行,而在这个等待时间内再次触发此函数,等待时间会重新计算。

应用场景

  1. 用户注册时候手机号码验证和邮箱验证只有等到用户输入完毕后,前端才需要检查格式是否正确,如果不正确,再弹出提示语。
  2. scroll/resize事件
  3. 文本连续输入,ajax验证/关键字搜索‘

实现方式

  • 函数防抖的要点,也是需要一个setTimeout来辅助实现。延迟执行需要跑的代码。
  • 如果方法多次触发,则把上次记录的延迟执行代码用clearTimeout清掉,重新开始。
  • 如果计时完毕,没有方法进来访问触发,则执行代码。
    举个例子: 在移动鼠标时触发打印函数 加上函数防抖。
var timer = false;
document.querySelector("#div").mousemove = function(){
    clearTimeout(timer);  // 当事件触发的时候,清除之前等待执行的函数,
    timer = setTimeout(function(){ //  开启新的延时执行函数
        console.log("函数防抖");
    }, 300);
};

我们现在对上述方法封装一下

// 函数防抖
const debounce = function(fn, wait=300){
    return function(){
        clearTimeout(fn.timer);  // 当事件触发的时候,清除之前等待执行的函数,
        fn.timer = setTimeout(fn, 300);
    }
}

但是我们就会发现 fn 的this 指向发生了改变, fn参数是接收不到的。

function debounce = function(fn, wait=300){
    return function(){
        clearTimeout(fn.timer);  // 当事件触发的时候,清除之前等待执行的函数,
        fn.timer = setTimeout(fn.bind(this), wait);
    }
}

或者使用apply

function debounce = function(fn, wait=100){
    return function(){
        clearTimeout(fn.timer);  // 当事件触发的时候,清除之前等待执行的函数,
        fn.timer = setTimeout( ()=>{ // 不能写匿名函数,this会发生改变
            return fn.apply(this, arguments);
        }, wait);
    }
}

函数节流

函数节流,即限制函数的执行频率,在持续触发事件的情况下,间断地执行函数。只要当前函数没有执行完成,任何新触发的函数都会被忽略。就是在固定的时间间隔内函数只会被调用一次。

适用场景

  1. 频繁的mousemove/keydown,比如高频的鼠标移动,游戏射击类的
  2. 搜索联想: 监听keypress事件,然后异步去查询结果. 如果快速输入过多字符串, 就会触发高频请求。
  3. 监听滚动事件判断是否到页面底部自动加载更多(scroll事件)

实现简易

  • 函数节流的要点是,声明一个变量当标志位,记录当前代码是否在执行。
  • 如果空闲,则可以正常触发方法执行。
  • 如果代码正在执行,则取消这次方法执行,直接return。
var timer = false;
document.querySelector("#div").mousemove = function(){
    if(timer)
        return;
    timer = setTimeout(function(){   //  正在执行
        console.log("函数节流");
        timer = false; // 表示不在执行
    }, 100);
};

在来改写一下:

const throttle = function(fn, wait=100){
    return function(){
        if(fn.timer){ return; }
        fn.timer = setTimeout(()=>{
            fn.apply(this,arguments);
            fn.timer = false;
        },wait)
    }
}

总结

函数的节流和函数的去抖都是通过减少实际逻辑处理过程的执行来提高事件处理函数运行性能的手段,并没有实质上减少事件的触发次数。
某些函数确实是用户主动调用的,但因为一些客观的原因,这些函数会严重地影响页面性能。解决方案:例如分页技术和延迟加载,都可以避免在页面上同时加载过多数据,造成的页面卡顿。甚至假死现象。其实,优化就是合理利用性能。

underscore v1.7.0相关的源码实现

_.debounce = function(func, wait, immediate) {
    // immediate默认为false
    var timeout, args, context, timestamp, result;

    var later = function() {
      // 当wait指定的时间间隔期间多次调用_.debounce返回的函数,则会不断更新timestamp的值,导致last < wait && last >= 0一直为true,从而不断启动新的计时器延时执行func
      var last = _.now() - timestamp;

      if (last < wait && last >= 0) {
        timeout = setTimeout(later, wait - last);
      } else {
        timeout = null;
        if (!immediate) {
          result = func.apply(context, args);
          if (!timeout) context = args = null;
        }
      }
    };

    return function() {
      context = this;
      args = arguments;
      timestamp = _.now();
      // 第一次调用该方法时,且immediate为true,则调用func函数
      var callNow = immediate && !timeout;
      // 在wait指定的时间间隔内首次调用该方法,则启动计时器定时调用func函数
      if (!timeout) timeout = setTimeout(later, wait);
      if (callNow) {
        result = func.apply(context, args);
        context = args = null;
      }

      return result;
    };
  };
_.throttle = function(func, wait, options) {
    /* options的默认值
     *  表示首次调用返回值方法时,会马上调用func;否则仅会记录当前时刻,当第二次调用的时间间隔超过wait时,才调用func。
     *  options.leading = true;
     * 表示当调用方法时,未到达wait指定的时间间隔,则启动计时器延迟调用func函数,若后续在既未达到wait指定的时间间隔和func函数又未被调用的情况下调用返回值方法,则被调用请求将被丢弃。
     *  options.trailing = true;
     * 注意:当options.trailing = false时,效果与上面的简单实现效果相同
     */
    var context, args, result;
    var timeout = null;
    var previous = 0;
    if (!options) options = {};
    var later = function() {
      previous = options.leading === false ? 0 : _.now();
      timeout = null;
      result = func.apply(context, args);
      if (!timeout) context = args = null;
    };
    return function() {
      var now = _.now();
      if (!previous && options.leading === false) previous = now;
      // 计算剩余时间
      var remaining = wait - (now - previous);
      context = this;
      args = arguments;
      // 当到达wait指定的时间间隔,则调用func函数
      // 精彩之处:按理来说remaining <= 0已经足够证明已经到达wait的时间间隔,但这里还考虑到假如客户端修改了系统时间则马上执行func函数。
      if (remaining <= 0 || remaining > wait) {
        // 由于setTimeout存在最小时间精度问题,因此会存在到达wait的时间间隔,但之前设置的setTimeout操作还没被执行,因此为保险起见,这里先清理setTimeout操作
        if (timeout) {
          clearTimeout(timeout);
          timeout = null;
        }
        previous = now;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      } else if (!timeout && options.trailing !== false) {
        // options.trailing=true时,延时执行func函数
        timeout = setTimeout(later, remaining);
      }
      return result;
    };
  };

参考[http://www.cnblogs.com/fsjohnhuang/p/4147810.html]

原文地址:https://www.cnblogs.com/cyrus-br/p/10529595.html

时间: 2024-10-09 05:25:47

函数节流和防抖的相关文章

js函数节流和防抖

// 函数节流 var canRun = true; document.getElementById("throttle").onscroll = function(){ if(!canRun){ // 判断是否已空闲,如果在执行中,则直接return return; } canRun = false; setTimeout(function(){ console.log("函数节流"); canRun = true; }, 300); }; // 函数防抖 var

js函数的节流和防抖

js函数的节流和防抖 用户浏览页面时会不可避免的触发一些高频度触发事件(例如页面 scroll ,屏幕 resize,监听用户输入等),这些事件会频繁触发浏览器的重拍(reflow)和重绘(repaint)这会严重耗费浏览器性能,造成页面 卡顿. 举几个例子:比如说我们在滚动事件中要做一个复杂的计算,或者做一个按钮的防二次点击操作的需求,这些需求都会在频繁的事件 回调中做复杂计算,很有可能导致页面卡顿,这时候我们可以将多次计算合并为一次计算,只在一个精确点做操作.这些事可以利用 函数的防抖来实现

JS 函数防抖和函数节流

我们都知道频繁触发执行一段js逻辑代码对性能会有很大的影响,尤其是在做一些效果实现方面,或者逻辑中需要进行后端请求,更是会导致卡顿,效果失效等结果,所以在处理类似的情况时,可以考虑使用函数节流和函数去抖来解决,至于具体使用哪一种方式,根据实际情况分析定夺,先来讲解一些这两者的概念 函数节流:在频繁触发的情况下,需要执行的逻辑只有执行完之后,才能继续执行下一次 函数防抖:在频繁触发的情况下,只有足够的空闲时间,才执行代码一次,如果没有执行完就清除掉,重新执行逻辑 应用场景:高频触发以下方法 页面滚

JS函数节流和函数防抖问题分析

问题1:如果实现了dom拖拽功能,但是在绑定拖拽事件的时候发现每当元素稍微移动一点便触发了大量的回调函数,导致浏览器直接卡死,这个时候怎么办? 问题2:如果给一个按钮绑定了表单提交的post事件,但是用户有些时候在网络情况极差的情况下多次点击按钮造成表单重复提交,如何防止多次提交的发生? 为了应对如上场景,便出现了 函数防抖 和 函数节流 两个概念,总的来说:这两个方法是在时间轴上控制函数的执行次数. 1.函数防抖(debounce) 概念: 在事件被触发n秒后再执行回调,如果在这n秒内又被触发

JavaScript函数节流和函数防抖的原理与区别

一.概念解释 ?函数节流和函数防抖,两者都是优化高频率执行js代码的一种手段.?大家大概都知道旧款电视机的工作原理,就是一行行得扫描出色彩到屏幕上,然后组成一张张图片.由于肉眼只能分辨出一定频率的变化,当高频率的扫描,人类是感觉不出来的.反而形成一种视觉效果,就是一张图.就像高速旋转的风扇,你看不到扇叶,只看到了一个圆一样.?同理,可以类推到js代码.在一定时间内,代码执行的次数不一定要非常多.达到一定频率就足够了.因为跑得越多,带来的效果也是一样.倒不如,把js代码的执行次数控制在合理的范围.

函数防抖与函数节流

前言 有一些浏览器事件我们不希望它很频繁的触发,如调整窗口大小(onresize).监听滚动条滚动(onscroll),如果这些监听事件需要调用接口的话一秒内可能会调用上百次,这样坑定是有问题的. 函数防抖(debounce) 如果有人进电梯(触发事件),那电梯将在10秒钟后出发(执行事件监听器),这时如果又有人进电梯了(在10秒内再次触发该事件),我们又得等10秒再出发(重新计时). function debounce(fn,wait){ var timer = null; return fu

JavaScript函数节流和函数防抖之间的区别

一.概念解释 ?函数节流和函数防抖,两者都是优化高频率执行js代码的一种手段.?大家大概都知道旧款电视机的工作原理,就是一行行得扫描出色彩到屏幕上,然后组成一张张图片.由于肉眼只能分辨出一定频率的变化,当高频率的扫描,人类是感觉不出来的.反而形成一种视觉效果,就是一张图.就像高速旋转的风扇,你看不到扇叶,只看到了一个圆一样.?同理,可以类推到js代码.在一定时间内,代码执行的次数不一定要非常多.达到一定频率就足够了.因为跑得越多,带来的效果也是一样.倒不如,把js代码的执行次数控制在合理的范围.

函数防抖和函数节流

函数防抖(debounce) ????什么是防抖?短时间内多次触发同一个事件,只执行最后一次,或者只在开始时执行,中间不执行. 使用防抖之绿色基础版 //绿色基础版: function debounce(doSomething,wait){ var timeout;//需要一个外部变量,为增强封装,所以使用闭包 return function(){ var _this = this, _arguments = arguments;//arguments中存着e clearTimeout(time

js前端性能优化之函数节流和函数防抖

前言:针对一些会频繁触发的事件如scroll.resize,如果正常绑定事件处理函数的话,有可能在很短的时间内多次连续触发事件,十分影响性能 节流: 节流:使得一定时间内只触发一次函数. 它和防抖动最大的区别就是,节流函数不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而防抖动只是在最后一次事件后才触发一次函数. 原理是通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器 主要有两种实现方法: 时间戳 定时器 时间戳实现: