防抖函数
在规定时间内重复执行同一个函数,但是只生效一次函数的行为。比如由于网络延迟,用户在一定时间内会重复点击某一个按钮,然而不断的执行按钮上的事件。若该事件是网络请求,则会给服务器不断造成压力,这是我们不愿看到的。
节流函数
在规定时间内重复执行同一个函数,但是按照一定的时间段生效函数的行为。和防抖函数非常相似。例如在支付回调后我们要确认支付的状态,可以手动检查+自动检查的情况下,我们使用节流函数是一个不错的选择。
相对于使用JavaScript封装这两个函数主要区别在于this的指向问题。使用JavaScript封装我们大可以使用apply在函数中指向,但是对于Typescript中的this单独在函数中使用却是大大受限制的。所以在typescript中我们使用class来解决这一问题。
代码演示:
// 防抖(ts)
class Debounced {
/**
* @param func 需要包装的函数
* @param delay 延迟时间,单位ms
* @param immediate 是否默认执行一次(第一次不延迟)
*/
public use = (func: Function, delay: number, immediate: boolean = false): Function => {
let timer: number | undefined
return ( ...args: any) => {
if (immediate) {
func.apply(this, args) // 确保引用函数的指向正确,并且函数的参数也不变
immediate = false
return
}
clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, delay)
}
}
}
// 节流(ts)
export class Throttle {
private timer: number | undefined
private stop: boolean = false
private death: boolean = false
/**
* @param func 需要包装的函数
* @param delay 延迟时间,单位ms
* @param immediate 是否默认执行一次(第一次不延迟)
*/
public use (func: Function, delay: number, immediate: boolean = false): Function {
let flag = true
const self = this
return (...args: any) => {
if (this.death) {
func.apply(this, args)
return
}
if (this.stop) {
func.apply(this, args)
return
}
if (immediate) {
func.apply(this, args)
immediate = false
return
}
if (!flag) {
return
}
flag = false
self.timer = setTimeout(() => {
func.apply(this, args)
flag = true
}, delay)
}
}
// 销毁
public destroy() {
this.death = true
this.stop = true
if (!!this.timer) {
clearTimeout(this.timer)
this.timer = undefined
}
}
// 开启
public open() {
if (!this.death) {
this.stop = false
}
}
// 关闭
public close() {
this.stop = true
}
}
使用:
<template>
<div id="debounced-test">
<v-button @click="handelClickByDebounced">防抖</v-button>
<v-button @click="handelClickByThrottle" type="success">节流</v-button>
<v-button @click="changeStopThrottle(throttle.stop)" type="warning">
{{ throttle.stop ? '开启' : '关闭' }}节流
</v-button>
<v-button @click="destroyThrottle" type="danger">销毁节流函数</v-button>
</div>
</template>
<script lang="ts">
import { Debounced, Throttle } from '@/utils'
import { Vue, Component } from 'vue-property-decorator'
@Component
export default class DebouncedTest extends Vue {
private count: number = 1
private debouncedUse: Function = new Debounced().use(this.request, 1000)
private throttle = new Throttle()
private throttleUse: Function = this.throttle.use(this.request, 1000)
private request(params: any) {
console.log('this的指向', this);
console.log('参数', params);
console.log(this.count++)
}
// 防抖调用
private handelClickByDebounced() {
this.debouncedUse(123)
}
// 节流调用
private handelClickByThrottle() {
this.throttleUse('截流函数')
}
// 停止 | 开启节流函数
private changeStopThrottle(action: boolean) {
action ? this.throttle.open() : this.throttle.close()
}
// 销毁节流函数
private destroyThrottle() {
this.throttle.destroy()
}
}
</script>
演示