javascript 防抖与节流
防抖
节流函数适用事件:
- window 的 resize、scroll
- mousedown、mousemove、keyup、keydown
- 表单事件 ...
防抖函数
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>
节流
节流函数适用事件:
- window 的 resize、scroll
- mousedown、mousemove
- 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>