UnderScore源码看防抖和节流

龟兔赛跑(快不必定好,慢也不必定差)

相信这事一个你们均可以耳熟能详的例子了,兔子跑得很快,这是他胜利的优点,可是同时也是"快"让它有了骄傲的思想,致使本身轻敌儿错失了胜利的机会。git

乌龟跑得很慢,可是他很谦虚,一步一个脚印,稳定向前抓住机遇取得了胜利!github

浏览器里的"龟兔赛跑"

咱们在浏览器中时常会遇到一些高频率事件:onscroll oninput resize onkeyup keydown... 那么当遇到这些的时候咱们到底应该让它像"兔子同样快速执行",仍是像"乌龟同样稳步向前"呢? 可是若是快速执行又可能会在较大的项目或者低版本浏览器中形成页面卡顿不流畅影响用户体验(如不理解请谷歌搜索浏览器渲染过程+重绘、回流)!而像乌龟同样稳步向前形成卡顿的概率会小不少,并且对于用户使用的体验基本毫无影响!因此咱们仍是稳步向前为好! 浏览器

那么怎么作到稳步执行呢?

优化高频事件 》》下降代码执行频率bash

那么怎么作到下降代码执行频率呢?

下降代码执行频率 》》 throttle(节流)|| debounce(防抖)app

throttle(节流)和 debounce(防抖)

请先记住两个概念:节流>间隔固定时间执行,防抖>你不中止我就不执行。请您记住,记住,记住!!! 不要再分不清这两个词语哪一个是哪一个了!!!!函数

throttle(节流)

直接上代码,我们先看一个简单版本容易理解的。优化

function throttle(fn, threshhold=150) {
var timeout;//方便清除定时器
var start = new Date;//开始的时间
return function () {
var context = this, args = arguments, curr = new Date() - 0
clearTimeout(timeout)//老是干掉事件回调(与后面的“让方法在脱离事件后也能执行一次”对应)
if(curr - start >= threshhold){ 
    fn.apply(context, args) 
    start = curr //其实到这里咱们就已经实现了节流的逻辑
}else{
   //让方法在脱离事件后也能执行一次(好比咱们最后一次点击一个按钮,点击后按照上面的逻辑当小于threshhold是不会执行此次点击事件的回调函数的,因此加上这个定时器确保最后一次不管间隔时间多大均可以执行)
    timeout = setTimeout(function(){
       fn.apply(context, args) 
    }, threshhold);
   }
 }
}
var logger = throttle(function(e) {
console.log(e.pageX, e.pageY)
});

// 绑定监听
document.querySelector("#btn").addEventListener('click',logger);
复制代码

下面看看UnderScore对throttle(节流)的封装ui

function throttle(func, wait, options) {;
      let args, context, previous = 0, timeout;
      let later = function () {//最后一次定时器中的回调
        func.apply(context, args);
        args = context = null
      }
      let throttled = function () {
        args = arguments;
        context = this;
        let now = Date.now(); // 如今的时间
        let remaning = wait - (now - previous);
        if (remaning <= 0) {
          if (timeout) {//屡次连续点击的时候就清除定时器,由于它不是最后一次点击,只有最后一次点击后才会保留定时器
            clearTimeout(timeout);
            timeout = null;
          }
          func.apply(context, args);
          previous = now;
        } else if (!timeout && options.trailing !== false) {//若是咱们不传trailing那么就是undefined一样不等于flase
        //先判断是否存在timeout,存在就不增长定时器,避免屡次定义定时器
          timeout = setTimeout(later, remaning);
        }
      }
      return throttled;
    }
    function logger() {
      console.log('logger');
    }
    //参数trailiing:达到让最后一次事件触发后回调方法仍是能执行的效果
    btn.addEventListener('click', throttle(logger, 1000, { trailiing: true }));
    
    //参数leading:达到让第一次事件触发后不马上执行回调
    function throttle(func, wait, options) {
      let args, context, previous = 0, timeout;
      let later = function () {
        previous = options.leading === false ? 0 : Date.now();
        //第二步:定时器回调中让previous回归正常
        func.apply(context, args);
        args = context = null
      }
      let throttled = function () {
        args = arguments;
        context = this;
        let now = Date.now();
        if (!previous && options.leading === false) previous = now;
        //第一步:使remaning必定大于0以此来达到让它走 else if,也就是定义定时器延迟处理事件。
        let remaning = wait - (now - previous);
        if (remaning <= 0) {
          if (timeout) {
            clearTimeout(timeout);
            timeout = null;
          }
          func.apply(context, args);
          previous = now;
        } else if (!timeout && options.trailing !== false) {
          timeout = setTimeout(later, remaning);
        }
      }
      return throttled;
    }
    function logger() {
      console.log('logger');
    }
    // btn.addEventListener('click', throttle(logger, 1000, { trailiing: true }));
    // 延迟第一次点击 是不生效的
    btn.addEventListener('click', throttle(logger, 1000, { leading: false }));
复制代码

此处是本身对UnderScore中throttle源码的简单重写。若有不懂的请私信我。。this

debounce(防抖)

防抖实现十分简单spa

function debounce(func,wait) {
      let timeout;
      return function () {
        clearTimeout(timeout); //有定时器就先清掉,始终保证只有一个定时器
        timeout = setTimeout(() => {
          func.apply(this,arguments);
          timeout = null;
        }, wait);
      }
    }
    function logger(e) {
      console.log('logger');
    }
    btn.addEventListener('click', debounce(logger,1000));
复制代码

可是呢,这样又有点bug,那就是咱们点击第一次也要等好久的定时器时间间隔才能够看到效果,而咱们有时会但愿的是点击了立刻就有效果。

下面看看UnderScore对 debounce(防抖)的封装

function debounce(func,wait,immediate) {
      let timeout;
      return function () {
        clearTimeout(timeout);
        if(immediate){
          let callNow = !timeout;  //第一次点击的话timeout就是undefined取反就是true,就会执行下一行,第二次点击的话就timeout不为空就不会按照原来的逻辑执行了。这样也就达到了点击第一次当即执行的效果。
          if(callNow) func.apply(this, arguments);
        }
        timeout = setTimeout(() => {
          func.apply(this,arguments);
          timeout = null;
        }, wait);
      }
    }
    function logger(e) {
      console.log('logger',e);
    }
    // 第三个参数 表示首次 先触发一下
    btn.addEventListener('click', debounce(logger,1000,true));
复制代码

上面就是UnderScore对节流、防抖的基本实现了,固然还有一个取消的方法,可是那个很简单能够自行去看一看

连接:github.com/jashkenas/u…

文章未完待续...

ToDo:(1)将文章思路再理一遍,配些动图和例子一步步的实现一下防抖节流

ToDo:(2)将lodash的防抖节流一步步实现一遍

PS:过年在家长膘,写得有点笼统 还望大佬们海涵

相关文章
相关标签/搜索