防抖
首先来看一下防抖的概念:
在频繁的触发事件执行函数时,让要执行的函数不断的往后延迟执行,直到没有新的事件触发时才会执行函数执行
应用场景:
- 输入框
- 页面滚动
- 页面缩放
- 窗口拖拽
1.基础实现
// 在封装一个函数时应该考虑的:
// -函数的参数
// -函数的返回值
// -内部实现
// 1.该函数接收一个函数和一个延迟执行时间
function mydebounce(fn, delay) {
// 定义定时器变量,清除上一次的函数使用
let timer = null;
// _debounce才是真正事件触发时的执行函数,用args接收所有传递的参数,如:event等
const _debounce = function(...args) {
// 闭包的运用场景,如果有再次触发事件,那就清除上一次事件触发的回调函数
if(timer) clearTimeout(timer)
// 定时器,每次有输入时就延迟delay时间后执行
timer = setTimeout(() => {
// 为要执行的fn绑定this和传递args参数,_debounce一定要用function定义,箭头函数没有this
fn.apply(this, args);
// 全部执行完毕后将timer还原为初始状态
timer = null;
}, delay);
};
return _debounce;
}
1. 取消函数执行功能
在函数执行过程中,如果想要取消这个函数执行,该怎么做呢?
答案:手动清除定时器
function mydebounce(fn, delay) {
let timer = null
const _debounce = function(...args) {
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, delay);
}
// 取消功能添加
_debounce.cancel = function() {
if(timer) clearTimeout(timer)
}
return _debounce
}
2. 立即执行功能
比如在购物网站搜索时,第一次输入直接执行一次函数去请求相关资源,该怎么才能再输入时立即执行呢?
答案:通过传入第三个参数immerdiate来判断,只有immediate为true时并且函数没有执行过才立即执行一次
function debounce(fn, delay, immediate = false) {
let timer = null
let isInvoke = false
const _debounce = function (...args) {
if (timer) clearTimeout(timer)
// 立即执行
if (immediate && !isInvoke) {
fn.apply(this, args)
isInvoke = true
return
}
// 延时执行
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
isInvoke = false
}, delay)
}
_debounce.cancel = function () {
if (timer) clearTimeout(timer)
}
return _debounce
}
全部代码示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text">
<button>取消函数执行</button>
<script>
function debounce(fn, delay, immediate = false) {
let timer = null
let isInvoke = false
const _debounce = function (...args) {
if (timer) clearTimeout(timer)
// 立即执行
if (immediate && !isInvoke) {
fn.apply(this, args)
isInvoke = true
return
}
// 延时执行
timer = setTimeout(() => {
fn.apply(this, args)
timer = null // 执行完成后把数据复原到初始的状态
isInvoke = false
}, delay)
}
_debounce.cancel = function () {
if (timer) clearTimeout(timer)
timer = null
isInvoke = false
}
return _debounce
}
const inputEl = document.querySelector("input")
const btnCancelEl = document.querySelector("button")
const debounceFn = debounce(function (event) {
console.log(this.value, event)
}, 2000, true)
inputEl.oninput = debounceFn
btnCancelEl.onclick = function () {
debounceFn.cancel()
}
</script>
</body>
</html>
节流
再看一下节流的概念
在频繁的触发事件执行函数时,让要执行的函数按固定的频率去执行。比如:每隔一段时间就执行一次
运用场景:
- 输入框
- 鼠标移动事件
- 用户频繁点击按钮操作
- 游戏中(如:飞机大战发射子弹)
1. 基础实现
const throttle = function(fn, interval) {
// 闭包保留函数输入的开始时间
let startTime = 0
const _throttle = function(...args) {
let nowTime = new Date().getTime()
// 核心逻辑,等待时间 = 频率时间-(当前时间 - 函数输入的开始时间)
const waitTime = interval - (nowTime - startTime)
// 当等待时间小于等于0时,说明到达了可以执行的频率时间
if(waitTime <= 0) {
fn.apply(this, args)
// 每一次执行完成后要将开始时间赋值为当前时间
startTime = nowTime
}
}
return _throttle
}
2. 立即执行控制
const throttle = function(fn, interval, immediate = true) {
let startTime = 0
const _throttle = function(...args) {
let nowTime = new Date().getTime()
// 立即执行控制,第一次输入时要不要立即执行,默认是执行的,当immediate为false时就不要立即执行
if(!immediate && startTime === 0) {
startTime = nowTime
}
const waitTime = interval - (nowTime - startTime)
if(waitTime <= 0) {
fn.apply(this, args)
startTime = nowTime
}
}
return _throttle
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text">
<script>
// 节流函数
const throttle = function(fn, interval, immediate = true) {
let startTime = 0
const _throttle = function(...args) {
let nowTime = new Date().getTime()
if(!immediate && startTime === 0) {
startTime = nowTime
}
const waitTime = interval - (nowTime - startTime)
if(waitTime <= 0) {
fn.apply(this, args)
startTime = nowTime
}
}
return _throttle
}
const inputEl = document.querySelector("input")
inputEl.oninput = throttle(function(event) {
console.log("输入事件响应", this.value, event)
}, 1000, false)
</script>
</body>
</html>