JS实现节流与防抖
1. 防抖
定义:在触发事件后,规定的事件内回调函数只能执行一次,如果在规定事件内又触发了该事件,则会重新开始计算规定事件
1.1 非立即执行版
事件触发 ->延时->执行回调函数。如果在延时中继续触发事件,则会重新进行延时。在延时结束后执行回调
代码及思路
function debounce(fn, delay) {
let timer = null;
return function() {
// 清除已存在的定时器
timer && clearTimeout(timer)
timer = setTimeout(() =>{//箭头函数里的this与return函数里的this一致,都是button按钮
fn.apply(this,arguments)
}, delay)
/*
如果使用的是普通函数,需要先使用变量保存return函数里的this,再指向它。
let that = this
timer = setTimeout(function(){
fn.apply(that,arguments)
}, delay)
*/
}
}
1.2 立即执行版
事件触发->执行回调函数->延时。如果在延时中继续触发事件,则会重新进行延时。在延时结束后不会执行回调
代码及思路
function debounce(fn, delay,immediately) {
let timer = null;
return function() {
// 清除已存在的定时器
timer && clearTimeout(timer)
if(immediately){ //根据immediately的值来确定是否立即执行
if(!timer){ //如果定时器不存在时,执行回调
fn.apply(this,arguments)
}
//不管上一个延时是否完成,都需要重置定时器
timer = setTimeout(() =>{
timer = null
}, delay)
}else{
timer = setTimeout(() =>{
fn.apply(this,arguments)
}, delay)
}
}
}
2. 节流
定义:在规定时间件内只能执行一次回调函数,如果在规定时间内又触发了该事件,则什么也不做
2.1 时间戳版
代码及思路
function throttle(fn, delay) {
// 记录上次触发的时间戳
let lastTime = 0;
return function() {
// 记录当前触发的时间戳
let nowTime = Date.now();
// 如果当前触发与上次触发的时间差值 大于 设置的周期则允许执行
if (nowTime - lastTime > delay) {
fn.apply(this,arguments);
// 更新时间戳
lastTime = nowTime;
}
}
}
2.2 定时器版
代码及思路
function throttle(fn, delay){
let timer = null;
return function(){
if(!timer){ //如果定时器为空,执行回调,规定时间内不再执行
fn.apply(this.arguments)
timer = setTimeout(()=>{
timer = null; //延时结束后,timer置为空
},delay)
}
}
}
3.防抖与节流的区别
- 防抖是将多次执行变为最后一次执行
- 节流是将多次执行变为在规定时间内只执行一次
4. 其他方法
除去自己封装防抖和节流函数外,还有许多JavaScript库已经为我们封装好了,这里介绍Loadsh
Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。
Lodash中文文档
4.1 _.debounce
_.debounce(func, [wait=0], [options=])
参数:
func
(Function): 要防抖动的函数。[wait=0]
(number): 需要延迟的毫秒数。[options=]
(Object): 选项对象。[options.leading=false]
(boolean): 指定在延迟开始前调用。[options.maxWait]
(number): 设置func
允许被延迟的最大值。[options.trailing=true]
(boolean): 指定在延迟结束后调用。
4.2 _.throttle
_.throttle(func, [wait=0], [options=])
参数:
func
(Function): 要节流的函数。[wait=0]
(number): 需要节流的毫秒。[options=]
(Object): 选项对象。[options.leading=true]
(boolean): 指定调用在节流开始前。[options.trailing=true]
(boolean): 指定调用在节流结束后。
4.3 使用方法
在HTML使用
<html>
<head>
<meta charset="utf-8">
<style>
</style>
<title></title>
</head>
<body>
<button>submit</button>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script type="text/javascript">
function coloring (){
let r = Math.floor(Math.random()*255)
let g = Math.floor(Math.random()*255)
let b = Math.floor(Math.random()*255)
document.body.style.backgroundColor = `rgb(${r},${g},${b})`
}
let btn = document.querySelector("button")
btn.addEventListener('click',_.debounce(coloring,1000,{
leading: true,
trailing: false
}))
window.addEventListener('resize',_.throttle(coloring,1000))
</script>
</body>
</html>
在vue中使用
npm i --save lodash
<template>
<div>
<button @click="debounce">click</button>
</div>
</template>
<script>
import _ from "lodash";
export default {
name: "Father",
methods: {
debounce: _.debounce(
function () { //这里如果使用箭头函数,this指向undefined
this.coloring();//这里的this指向的就是vue实例
},
2000,
{
leading: true,
trailing: false,
}
),
coloring() {
let r = Math.floor(Math.random() * 255);
let g = Math.floor(Math.random() * 255);
let b = Math.floor(Math.random() * 255);
document.body.style.backgroundColor = `rgb(${r},${g},${b})`;
},
},
created() {
window.addEventListener("resize", _.throttle(this.coloring, 2000));
},
};
</script>
<style lang="less" scoped>
</style>