不一样的函数防抖和节流

防抖和节流

浏览器的 resize、scroll、keypress、mousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能。为了优化体验,需要对这类事件进行调用次数的限制。

  • 狂点一个按钮
  • 页面滚动
  • 入模糊匹配
  • 。。。。。。

防抖(debounce)

在某一次高频触发下,我们只识别一次(可以控制开始触发,还是最后一次触发)
详细:假设我们规定500MS触发多次算是高频,只要我们检测到是高频触发了,则在本次频繁操作下(哪怕你操作了10次)也是只触发一次…


function () {
     
  console.log("hello world")
}

上面按钮每一次点击,都会触发打印,如果疯狂点击,那么下面就会疯狂打印,会降低性能。

实际开发1:使用按钮只会,移除事件绑定


function handle() {
     
    submit.onclick = null;
    submit.disabled = true;
    console.log('OK');
    setTimeout(() => {
     
      submit.onclick = handle;
      submit.disabled = false;
    }, 1000);
}
submit.onclick = handle;

实际开发2:使用按钮只会,移除事件绑定


let flag = false;
submit.onclick = function () {
     
  if (flag) return;
  flag = true;
  console.log('OK');
  setTimeout(() => {
     
    // 事情处理完
    flag = false;
  }, 1000);
};

封装一个函数防抖的方法


function debounce(func, wait, immediate) {
     
  // 多个参数及传递默认的处理
  if (typeof func !== "function") throw new TypeError("func must be an function!");
  if (typeof wait === "undefined") wait = 500;
  if (typeof wait === "boolean") {
     
    immediate = wait;
    wait = 500;
  }
  if (typeof immediate !== "boolean") immediate = false;

  // 设定定时器返回值标识
  let timer = null;
  return function proxy(...params) {
     
    let self = this,
      now = immediate && !timer;

    clearTimeout(timer);
    timer = setTimeout(function () {
     
      timer = null;
      !immediate ? func.call(self, ...params) : null;
    }, wait);

    // 第一次触发就立即执行
    now ? func.call(self, ...params) : null;
  };
}

function handle(ev) {
     
  // 具体在点击的时候要处理的业务
  console.log('OK', this, ev);
}
submit.onclick = debounce(handle, true);
// submit.onclick = proxy;  疯狂点击的情况下,proxy会被疯狂执行,我们需要在proxy中根据频率管控handle的执行次数
// submit.onclick = handle; //handle->this:submit  传递一个事件对象

简洁版本的防抖

function myDebounce(fn, interval = 500) {
     
  let timeout = null;
  return function () {
     
    clearTimeout(timeout);
    timeout = setTimeout(() => {
     
      fn.apply(this, arguments);
    }, interval);
  };
}
function doSomething(){
     
    // onmousemove触发的事件回调函数
}
container.onmousemove = myDebounce(doSomething, 300);

节流(throttle)

在某一次高频触发下,我们不是只识别一次,按照我们设定的间隔时间(自己规定的频率),没到达这个频率都会触发一次
详细:假设我们规定频率是500MS,我们操作了10min,触发的次数=(10601000)/500

比如,滚动页面,触发了多次打印

body {
  height: 3000px;
  overflow-x: hidden;
  background: -webkit-linear-gradient(top left, lightblue, pink, orange);
}
window.onscroll = function () {
     
  // 默认情况下,页面滚动中:浏览器在最快的反应时间内(4~6MS),就会识别监听一次事件触发,把绑定的方法执行,这样导致方法执行的次数过多,造成不必要的资源浪费
  console.log('OK');
};

封装一个函数节流的方法

body {
  height: 3000px;
  overflow-x: hidden;
  background: -webkit-linear-gradient(top left, lightblue, pink, orange);
}
function throttle(func, wait) {
     
  if (typeof func !== "function") throw new TypeError("func must be an function!");
  if (typeof wait === "undefined") wait = 500;
  let timer = null,
    previous = 0; //记录上一次操作的时间
  return function proxy(...params) {
     
    let self = this,
      now = new Date(), //当前这次触发操作的时间
      remaining = wait - (now - previous);
    if (remaining <= 0) {
     
      // 两次间隔时间超过wait了,直接执行即可
      clearTimeout(timer);
      timer = null;
      previous = now;
      func.call(self, ...params);
    } else if (!timer) {
     
      // 两次触发的间隔时间没有超过wait,则设置定时器,让其等待remaining这么久之后执行一次「前提:没有设置过定时器」
      timer = setTimeout(function () {
     
        clearTimeout(timer);
        timer = null;
        previous = new Date();
        func.call(self, ...params);
      }, remaining);
    }
  };
}

function handle() {
     
  console.log('OK');
}
window.onscroll = throttle(handle, 500);

简洁版本的节流

function myThrottle(fn, interval = 500) {
     
  let run = true;
  return function () {
     
    if (!run) return;
    run = false;
    setTimeout(() => {
     
      fn.apply(this, arguments);
      run = true;
    }, interval);
  };
}

function doSomething(){
     
    // onmousemove触发的事件回调函数
}
container.onmousemove = myThrottle(doSomething, 300);

你可能感兴趣的:(高级前端编程)