一、防抖函数代码实现
防抖:适用于高频函数的【延迟执行】,比如搜索框的联想功能
0、测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input type="text" /> <button>取消</button>
<script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script>
<script src="./04_debounce-cancel.js"></script>
<script>
const inputEl = document.querySelector("input");
let counter = 0;
const handleInputChange = function (event) {
console.log(`函数被调用了 ${++counter} 次`, this, event);
return "aaaaa";
};
// const debounceFunc = _.debounce(handleInputChange, 2000);
const debounceFunc = debounce(handleInputChange, 3000, true, (res) => {
console.log("callback:", res);
});
inputEl.oninput = function (...arg) {
const promise = debounceFunc.apply(this, arg);
promise
.then((res) => {
console.log("promise:", res);
})
.catch((err) => {
console.log(err);
});
};
const buttonEl = document.querySelector("button");
buttonEl.onclick = function (event) {
debounceFunc.cancel();
};
</script>
</body>
</html>
1、最基本的防抖函数实现【版本一】
function debounce(fn, delay) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null
// 2.真正执行的函数
const _debounce = function() {
// 取消上一次的定时器
if (timer) clearTimeout(timer)
// 延迟执行
timer = setTimeout(() => {
// 外部传入的真正要执行的函数
fn()
}, delay)
}
return _debounce
}
2、解决 func内部使用this和event都会丢失【版本二】
function debounce(fn, delay) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null
// 2.真正执行的函数
const _debounce = function(...args) {
// 取消上一次的定时器
if (timer) clearTimeout(timer)
// 延迟执行
timer = setTimeout(() => {
// 外部传入的真正要执行的函数
fn.apply(this, args)
}, delay)
}
return _debounce
}
3、增加需求,防抖函数首次立即执行【版本三】
function debounce(fn, delay, immediate = false) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null
let isInvoke = false
// 2.真正执行的函数
const _debounce = function(...args) {
// 取消上一次的定时器
if (timer) clearTimeout(timer)
// 判断是否需要立即执行
if (immediate && !isInvoke) {
fn.apply(this, args)
isInvoke = true
} else {
// 延迟执行
timer = setTimeout(() => {
// 外部传入的真正要执行的函数
fn.apply(this, args)
isInvoke = false
}, delay)
}
}
return _debounce
}
4、增加需求,给防抖函数增加一个取消功能【最终版本四】
function debounce(fn, delay, immediate = false) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null
let isInvoke = false
// 2.真正执行的函数
const _debounce = function(...args) {
// 取消上一次的定时器
if (timer) clearTimeout(timer)
// 判断是否需要立即执行
if (immediate && !isInvoke) {
fn.apply(this, args)
isInvoke = true
} else {
// 延迟执行
timer = setTimeout(() => {
// 外部传入的真正要执行的函数
fn.apply(this, args)
isInvoke = false
timer = null
}, delay)
}
}
// 封装取消功能
_debounce.cancel = function() {
if (timer) clearTimeout(timer)
timer = null
isInvoke = false
}
return _debounce
}
5、增加需求,给防抖函数增加获取返回值功能【特殊版本五】,有哪两种拿返回值的思路?
// 1.定义一个定时器, 保存上一次的定时器
let timer = null
let isInvoke = false
// 2.真正执行的函数
const _debounce = function(...args) {
return new Promise((resolve, reject) => {
// 取消上一次的定时器
if (timer) clearTimeout(timer)
// 判断是否需要立即执行
if (immediate && !isInvoke) {
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
resolve(result)
isInvoke = true
} else {
// 延迟执行
timer = setTimeout(() => {
// 外部传入的真正要执行的函数
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
resolve(result)
isInvoke = false
timer = null
}, delay)
}
})
}
// 封装取消功能
_debounce.cancel = function() {
if (timer) clearTimeout(timer)
timer = null
isInvoke = false
}
return _debounce
}
二、节流函数代码实现
节流:适用于高频函数的【分时间段执行】,比如飞机大战里面的按键发射子弹功能
0、测试html代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button>发射</button>
<script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script>
<script src="./02_节流首次执行可配.js"></script>
<script>
const buttonEl = document.querySelector("button");
let counter = 0;
const handleShot = function (event) {
console.log(`子弹发射${++counter}次`, this, event);
};
const throttleFunc = throttle(handleShot, 2000, {
leading: false,
trailing: true,
});
buttonEl.onclick = throttleFunc;
</script>
</body>
</html>
1、节流函数最简版实现【版本一】
function throttle(func, interval) {
let lastTime = 0;
const throttleFunc = function () {
let currentTime = new Date().getTime();
let remainTime = interval - (currentTime - lastTime);
if (remainTime < 0) {
func();
lastTime = currentTime;
}
};
return throttleFunc;
}
2、节流函数首次执行可配置【版本二,一般用此版本即可】
function throttle(func, interval, options = { leading: true }) {
let { leading } = options;
let lastTime = 0;
const throttleFunc = function (...args) {
if (!leading && lastTime === 0) {
lastTime = new Date().getTime();
}
let currentTime = new Date().getTime();
let remainTime = interval - (currentTime - lastTime);
if (remainTime < 0) {
func.apply(this, args);
lastTime = currentTime;
}
};
return throttleFunc;
}
3、节流函数末次执行可配置【版本三】,取消功能和返回值功能参照防抖函数实现。
function throttle(
func,
interval,
options = { leading: true, trailing: false }
) {
let { leading, trailing } = options;
let lastTime = 0;
let timer = null;
const throttleFunc = function () {
if (!leading && lastTime === 0) {
lastTime = new Date().getTime();
}
let currentTime = new Date().getTime();
let remainTime = interval - (currentTime - lastTime);
if (timer) clearTimeout(timer);
if (remainTime <= 0) {
func();
lastTime = currentTime;
} else if (trailing) {
timer = setTimeout(() => {
func();
lastTime = new Date().getTime();
}, remainTime);
}
};
return throttleFunc;
}