Skip to content
此页目录
本文总阅读量

javascript 防抖与节流

防抖

节流函数适用事件:

  1. window 的 resize、scroll
  2. mousedown、mousemove、keyup、keydown
  3. 表单事件 ...

防抖函数

ts
/* 
  @params {Function} fun 要执行的方法
  @params {Number?} wait 间隔时间
  @params {Boolean?} immediate 立即执行一次
  @return {Function} 防抖函数
      Function.status  true: 开启防抖(default); false: 关闭防抖
*/
export function debounce(
  this: any,
  fun: (...args: any[]) => void,
  wait = 0,
  immediate = false
) {
  let timer: any, // 定时器
    prevTimestamp: number, // 触发时的时间戳
    ctx: any, // 上下文
    args: any // 参数
  const run = (delay: number) => {
    timer = setTimeout(() => {
      /* 此定时器为一轮循环第一次触发 */
      const now = Date.now()
      const interval = now - prevTimestamp // 上一次触发时的间隔
      // 在当前定时上下文时,prevTimestamp 可能被再次赋值
      if (interval < delay) {
        prevTimestamp = now
        run(wait - interval) // 本应该延迟时长 - 当前过去的时长 = 下一次延迟触发
        return
      }
      fun.apply(ctx, args)
      timer = null
    }, delay)
  }

  if (immediate) fun.apply(this, arguments)

  function temp(this: any) {
    if (!temp.status) return fun.apply(this, arguments)
    prevTimestamp = Date.now()

    if (timer) return // 延迟存在则不触发,只记录当前触发时间戳
    ctx = this
    args = arguments
    run(wait)
  }
  temp.status = true
  return temp
}

绑定事件

js
inp.addEventListener( "input",debounce(function () {
  console.log(this, arguments);
},1000));

立即执行

js
const ctx = debounce(function () {
  console.log( this, arguments);
},1000,true)
inp.addEventListener( "input",ctx);

取消防抖

js
const ctx = debounce(function () {
  console.log("test", this, arguments);
},1000,true)
inp.addEventListener( "input",ctx,);
ctx.status = false;

Demo

handlerInput.status 节流状态:true 延迟时间: ms

点击查看 Demo 代码
vue
<template>
  <div>
    <input
      v-model="keyword"
      type="text"
      placeholder="输入文字测试"
      @input="handlerInput"
    />
    <p>
      handlerInput.status 节流状态:{{ status }}
      <button @click="setStatus(true)">开启节流</button>
      <button @click="setStatus(false)">关闭节流</button>
      延迟时间:
      <input
        v-model="wait"
        type="number"
        @input="setWait"
      />
      ms
    </p>
    <ul>
      <li
        v-for="(i, index) of arr"
        :key="index"
      >
        {{ i }}
      </li>
    </ul>
  </div>
</template>

<script>
import { debounce } from "./debounce"
export default {
  data() {
    return {
      wait: 500,
      status: true,
      arr: [],
      keyword: "",
    }
  },
  created() {
    this.setWait(this.wait)
  },
  methods: {
    setWait() {
      this.handlerInput = debounce(() => {
        this.arr.unshift(this.keyword)
      }, this.wait)
    },
    setStatus(status) {
      this.handlerInput.status = status
      this.status = status
    },
  },
}
</script>
<style lang="scss" scoped>
ul {
  width: 100%;
  height: 200px;
  background-color: var(--c-details-bg);
  overflow: auto;
  border-radius: 5px;
  font-size: 18px;
}
</style>

节流

节流函数适用事件:

  1. window 的 resize、scroll
  2. mousedown、mousemove
  3. keyup、keydown ...

即在连续触发的事件中,一段时间内只触发一次。

节流函数

ts
/* 
  @params {Function} fun 要执行的方法
  @params {Number?} wait 间隔时间
  @params {Boolean?} immediate 立即执行一次
  @return {Function} 节流函数
      Function.status  true: 开启节流(default); false: 关闭节流
*/
export function throttle(
  fun: (...args: any[]) => void,
  wait = 0,
  immediate = false
) {
  let prev = Date.now() // 记录上一次执行时间
  if (immediate) temp() // 立即执行
  function temp(this: any) {
    const now = Date.now() // 当前执行时间
    let result
    if (now - prev >= wait || immediate || !temp.status) {
      // 超过间隔时间 || 第一次立即执行 || 取消节流
      result = fun.apply(this, arguments) // 处理 this 指向问题,及传入参数、event 事件对象,函数返回值
      prev = now
      if (immediate) immediate = false // 立即执行后设为 false
    }
    return result // 函数返回值
  }
  temp.status = true
  return temp
}

绑定事件

js
// 一秒内执行一次
dom.addEventListener(
  "mousemove",
  throttle(function () {
    console.log(this, event);
  }, 1000)
);

立即执行

js
const ctx = throttle(
  function () {
    console.log(this);
  },
  1000,
  true, // 创建节流函数时就执行一次
);

传参、返回值

js
const ctx = throttle(
  function (str) {
    return str + ' world'
  },
  0,
);
console.log(ctx('hello')); // hello world

取消节流

js
const ctx = throttle(
  function (str) {
    return str + ' world'
  },
  1000,
);
ctx.status = false; // false 关闭节流状态,默认 true

demo

0

handlerMove.status 节流状态:true 间隔时间: ms

点击查看 Demo 代码
vue
<template>
  <div>
    <div
      class="throttle"
      @mousemove="handlerMove"
    >
      {{ num }}
    </div>
    <p>
      handlerMove.status 节流状态:{{ status }}
      <button @click="setStatus(true)">开启节流</button>
      <button @click="setStatus(false)">关闭节流</button>
      间隔时间:
      <input
        type="number"
        v-model="wait"
        @input="setWait"
      />
      ms
    </p>
  </div>
</template>

<script>
import { throttle } from "./throttle"
export default {
  data() {
    return {
      num: 0,
      wait: 1000,
      status: true,
    }
  },
  created() {
    this.setWait(this.wait)
  },
  methods: {
    setWait() {
      this.handlerMove = throttle(() => {
        // console.log(this, event);
        this.num++
      }, this.wait)
    },
    setStatus(status) {
      this.handlerMove.status = status
      this.status = status
    },
  },
}
</script>
<style lang="scss" scoped>
.throttle {
  width: 100%;
  height: 200px;
  background-color: var(--c-details-bg);
  overflow: auto;
  border-radius: 5px;
  font-size: 50px;
  text-align: center;
  line-height: 200px;
}
</style>

评论

交流群