# 介绍防抖节流原理,区别以及应用,并用 JavaScript 进行实现

# 上期回顾

# HTTP 缓存策略,及解决的问题

  • 强制缓存>协商缓存
  • 强制缓存:cache-control > expires 【都不知道版本号】
  • 协商缓存: Etag/If-None-Match > Last-Modified/If-Modified-Since Etag 的计算成本,modified 的 1s 多次发布定位不了【只精确到 s】

# 今日解题

# 1,防抖

  • 原理:在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发 ,则重新计时。
  • 适用场景:
    • 按钮提交场景:防止多次提交按钮,只执行最后一次
    • 搜索框联想场景:防止联想发送请求,只发送最后一次输入

# 简易版本

function debounce(func, wait) {
  let timeout;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timeout);
    timeout = setTimeout(function() {
      func.apply(context, args);
    }, wait);
  };
}

# 立即执行版本

立即执行函数,然后等到停止触发 n 秒后,才可以重新触发执行

function debounce(func, wait, immediate) {
  let timeout;
  return function() {
    const context = this;
    const arg = arguments;
    if (timeout) {
      clearTimeout(timeout);
    }
    if (immediate) {
      const callNow = !timeout;
      timeout = setTimeout(function() {
        timeout = null;
      }, wait);
      if (callNow) func.apply(context, args);
    } else {
      timeout = setTimeout(function() {
        func.apply(context, args);
      }, wait);
    }
  };
}
// <button id="btn">btn-do-do</button>
// <h1 id="container"></h1>
var count = 1;
var container = document.getElementById("container");
var btn = document.getElementById("btn");

function getUserAction() {
  console.log(new Date().valueOf());
  container.innerHTML = count++;
}
var a = debounce(getUserAction, 2000, false); // important timeout被初始化
btn.onclick = () => {
  a(); // 此时timeout共享
};

# 返回值版本

function debounce(func, wait, immediate) {
  let timeout, result;
  return function() {
    const context = this;
    const args = arguments;
    if (timeout) {
      clearTimeout(timeout);
    }
    if (immediate) {
      const callNow = !timeout;
      timeout = setTimeout(function() {
        timeout = null;
      }, wait);
      if (callNow) result = func.apply(context, args);
    } else {
      timeout = setTimeout(function() {
        func.apply(context, args);
      }, wait);
    }
    return result;
  };
}
const debounce = (fun, delay = 500) => {
  let timer = null;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(function() {
      fun.apply(this, args);
    }, delay);
  };
};

# 2,节流

  • 原理:规定在一个单位时间内,只能触发一次函数,如果这个单位时间内触发多次函数,只能一次生效。
  • 适用场景
    • 拖拽场景:固定时间内只执行一次,防止超高频次触发位置变动
    • 缩放场景:监控浏览器 resize

# 使用时间戳实现

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

# 使用定时器

function throttle(func, wait) {
  let timeout;
  return function() {
    const context = this;
    const args = arguments;
    if (!timeout) {
      timeout = setTimeout(function() {
        timeout = null;
        func.apply(context, args);
      }, wait);
    }
  };
}
const throttle = (fun, delay = 1000) => {
  let flag = true;
  return function(...args) {
    if (!flag) return;
    flag = false;
    setTimeout(() => {
      fun.apply(this, args);
      flag = true;
    }, delay);
  };
};

# 区别:

节流不管事件触发多频繁保证在一定时间内一定会执行一次函数,而防抖是只在最后一次事件触发后才会执行一次函数。