防抖And节流

1.防抖(debounce )

防抖的原理就是:你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行,真是任性呐!

第一版

    var count = 1;
    var container = document.querySelector('.container');
    function getUserAction() {
      console.log(this);//指向Window对象
      container.innerHTML = count++;
    }
    function debounce (){
      var timeout
      return function(){
          if (timeout) clearTimeout(timeout);
          timeout = setTimeout(func, wait);
      }
    }
    container.onmousemove = debounce(getUserAction, 1000);

我们可以看到getUserAction函数的this指向的是window对象,如果我们想指向

我们该如何做呢?
第二版

   var count = 1;
   var container = document.querySelector('.container');
   function getUserAction() {
      console.log(this); //指向 
container.innerHTML = count++; } function debounce(func, wait) { var timeout, context; return function () { context = this; if (timeout) clearTimeout(timeout); timeout = setTimeout(() => func.apply(this), wait); }; } container.onmousemove = debounce(getUserAction, 1000);

我们利用apply方法改变了this的指向,从而getUserAction方法可以正确指向调用的的对象,那么问题又来了,我们在使用js操作dom元素的时候,函数会给我传一个事件对象,在我们第二版的函数中,如果直接输出参数,会看到输出undefined,想输出时间对象,我们应该如何做呢?
第三版

 var count = 1;
    var container = document.querySelector('.container');

    function getUserAction(e) {
      console.log(this); //指向 
console.log(e); //输出事件对象 container.innerHTML = count++; } function debounce(func, wait) { var timeout, context, args; return function () { context = this; args = arguments; if (timeout) clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, arguments), wait); }; } container.onmousemove = debounce(getUserAction, 1000);

这个时候,代码已经很是完善了,但是为了让这个函数更加完善,我们接下来思考一个新的需求。
这个需求就是:
我不希望非要等到事件停止触发后才执行,我希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行。
想想这个需求也是很有道理的嘛,那我们加个 immediate 参数判断是否是立刻执行。
第四版(立即执行)

  var count = 1;
    var container = document.querySelector('.container');

    function getUserAction(e) {
      console.log(this); //指向 
console.log(e); //输出事件对象 container.innerHTML = count++; } function debounce(func, wait, immediate) { var timeout, context, args; return function () { context = this; args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { var callnow = !timeout; console.log(callnow); timeout = setTimeout(() => { timeout = null; }, wait); if (callnow) { func.apply(this, args); } } else { timeout = setTimeout(() => func.apply(this, arguments), wait); } }; } container.onmousemove = debounce(getUserAction, 1000, true);

image.png

从图中可以看出立即执行非立即执行函数的原理区别在于:
立即执行函数:timeout为undefined,所以 callnow第一次为true,当用户一直移动鼠标的时候,callnow都为false,当用户停止移动鼠标的时候,以下代码执行,将timeout变为null,所以当用户再次移动鼠标的时候,
timeout变为null,callnow又会变为true,

 timeout = setTimeout(() => {
            timeout = null;
          }, wait);
2.节流

节流的原理很简单:
如果你持续触发事件,每隔一段时间,只执行一次事件。
根据首次是否执行以及结束后是否执行,效果有所不同,实现的方式也有所不同。
我们用 leading 代表首次是否执行,trailing 代表结束后是否再执行一次。
关于节流的实现,有两种主流的实现方式,一种是使用时间戳,一种是设置定时器。
时间戳

function throttle(func, wait) {
      var previous = 0;
      return function () {
        let now = Date.now();
        let context = this;
        let args = arguments;
        if (now - previous > wait) {
          func.apply(this, arguments);
          previous = now;
        }
      };
    }

定时器

 function throttle(func, wait) {
      var timeout;
      return function () {
        var context = this;
        if (!timeout) {
          timeout = setTimeout(() => {
            func.apply(context);
            timeout = null;
          }, wait);
        }
      };
    }

最后贴上大神链接
节流
防抖

你可能感兴趣的:(防抖And节流)