JavaScript函数防抖与截流

一、在前端开发当中,有些交互事件,会频繁触发。这样会导致我们的页面渲染性能,如果频繁触发接口调用的话,会直接导致服务器性能的浪费。

     例如:键盘事件 keyup作为测试

<ul>
  <li>
    未做处理:<input type="text" id="pt">
    <div>函数被调用:<span id="count">0</span>次</div>
  </li>
</ul>
const count = document.getElementById('count');
const pt = document.getElementById('ipt');
let init = 0
pt.onkeyup = function() { 
  count.innerText = ++init
}

运行效果

       每次输入都会触发事件的执行。如果我们用这样的方式去检测:当前用户输入的用户名是否可用?如此高频率的触发不仅是极大的浪费,而且用户还没有输入完成就检测,对用户的提示也不好。应该等用户输入完了,我们再去触发函数,下面我们优化一下:

<ul>
    <li>
      未做处理:<input type="text" id="pt">
      <div>函数被调用:<span id="count">0</span>次</div>
    </li>
    <li>
      防抖处理:<input type="text" id="pt2">
      <div>函数被调用:<span id="count2">0</span>次</div>
    </li>
</ul>
const Count2 = document.getElementById('count2');
const pt2 = document.getElementById('pt2');
// 设置一个默认值 500ms
const debounce = (fn, wait = 500) => {
  let time = null
  return function(arguments) {
    const _this = this, args = arguments
    clearTimeout(time)
    time = setTimeout(() => {
      fn.apply(_this, [args])
}, wait)
  }}
let init2 = pt2.onkeyup = debounce(function() {
Count2.innerText = ++init2

运行效果:

       可以看到,加了防抖函数之后,当我们在频繁输入的时候,函数并没有执行, 只有在函数指定的间隔内(500ms)不再输入了,才会执行函数。如果在时间间隔之内继续输入,会触发函数重新计数。

       函数防抖:在事件触发后的n秒之后,再去执行真正需要执行的函数,如果在这n秒之内事件又被触发,则重新开始计时

       也就是说,如果用户在间隔时间内一直触发函数,那么这个防抖函数内部的真正需要执行的函数将永远无法执行。

       那有没有什么好点的办法,让用户在输入过程中,既能触发真实需要的函数,又能达到优化的效果?

     答案是肯定的,那就是:

      函数截流:规定好一个单位时间,触发函数一次。如果在这个单位时间内触发多次函数的话,只有一次是可被执行的。想执行多次的话,只能等到下一个周期里

<li>
   截流处理:<input type="text" id="pt3">
   <div>函数被调用:<span id="count3">0</span>次</div>
   <div>当前时间(分/秒):<span id="time"></span></div>
</li>

 

const count3 = document.getElementById('count3');
const pt3 = document.getElementById('pt3');
const time = document.getElementById('time');
const throttle = (fn, hold = 1000) => {
  let last, deferTimer
  return function(arguments) {
    const _this = this, args = arguments
    let now = +new Date()
    if(last && now < last + hold) {
      clearTimeout(deferTimer)
      deferTimer = setTimeout(function () {
          last = now
          fn.call(_this, args)
        }, hold)
    } else {
       last = now
         fn.call(_this, args)
    }
  }}
let init3 = 0
const Ipt = throttle(function() {
let time = new Date().getMinutes() + ':' + new Date().getSeconds()
    time.innerText = time
   count3.innerText = ++init3
}, 1000)  // 初始化一下

oIpt3.onkeyup = function() {
  Ipt()
}

二、应用场景:防抖和截流都是用来防止高频率的js代码的执行

   1、防抖:防抖本质上就是以最后的操作为标准

例如:此时此刻我们都在排队等公交,司机说必须等到坐满才会发车,这时候

的参照标准就是最后一个人上车,公交车好比我们的js代码,最后一个人就充当我们的

执行条件。例如:

let setTimer;
let shake = function() {
  clearTimeout(setTimer);
  setTimer = setTimeout(() => {
    console.log("这里是实际的业务代码");
  }, 0);
};
let interTimer = setInterval(() => {
  shake();
}, 0);
let timer = setTimeout(() => {
  clearInterval(interTimer);
  clearTimeout(timer);
  timer = null;
  interTimer = null;
}, 2000);

执行以上代码,控制台会在 2s 后打出日志,2s 之内的操作都被清空,以最后一次的操作为准。

        如果监听滚动事件,假设两秒以内用户在不断的平凡的触发onScroll事件,只有用户暂停滚动后,才会去执行响应的操作,代码如下

// 函数防抖
var timer = false;
document.getElementById("xxxx").onscroll = function(){
    clearTimeout(timer); // 清除未执行的代码,重置回初始化状态
    timer = setTimeout(function(){
        console.log("函数防抖");
    }, 300);
};

     2、截流:

         (1)定时器实现截流

let isAllow = true;
function shake() {
  let fun = function() {
    if (!isAllow) return;
    isAllow = false;
    let timer = setTimeout(() => {
      console.log("这里是实际的业务代码");
      clearTimeout(timer);
      timer = null;
      isAllow = true;
    }, 1000);
  };
  fun();
}
let interTimer = setInterval(() => {
  shake();
}, 0);

   执行以上会看到控制台每隔 1s 后打印出结果,1s 内不会执行打印日志

          (2)闭包实现函数截流:

// fn是我们需要包装的事件回调, interval是时间间隔的阈值
function fun(fn, interval) {
    let last = 0; // last为上一次触发回调的时间
    // 将throttle处理结果当作函数返回
    return function() {
      let context = this; // 保留调用时的this上下文
      let args = arguments; // 保留调用时传入的参数
      let now = +new Date(); // 记录本次触发回调的时间
      // 判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值
      if (now - last >= interval) {
        // 如果时间间隔大于我们设定的时间间隔阈值,则执行回调
        last = now;
        fn.apply(context, args);
      }
    };
  }
  // 用fun来包装scroll的回调
  const better_scroll = fun(() => console.log("触发了滚动事件"), 1000);
  setInterval(() => better_scroll(), 0);
  

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上庸者-不服周

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值