前言
面试中经常会被问到防抖和节流函数,本文主要讲解下防抖和节流函数的简单实现。
一、防抖
函数防抖是指触发高频事件n秒后函数会执行一次,如果n秒内高频事件被再次触发,则重新计算时间;在整个过程中,事件函数只会被执行一次。
使用场景:onMouseOver,onMouseMove,resize、input等
1. 非立即执行版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>防抖</title>
</head>
<body>
<div style="height: 1000px;width: 100%;">打开控制台 滚动页面 开始测试吧</div>
<script>
// 防抖功能函数
function debounce (func, ms = 1000) {
// 创建一个标记用来存放定时器的返回值
let timer
return function (...args) {
// 每次当用户触发事件的时候,把前一个定时器清除
if (timer) {
clearTimeout(timer)
}
// 然后创建一个新的setTimeout
// 这样就能保证触发函数的间隔时间内再次触发的话,就不会执行func函数
timer = setTimeout(() => {
func.apply(this, args)
}, ms)
}
}
// 有些需要防抖的工作,在这里执行
const task = () => {
console.log('执行了 防抖么')
}
const handlerScroll = debounce(task, 1000)
document.addEventListener('scroll', handlerScroll)
</script>
</body>
</html>
2. 立即执行版
<!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>
<div style="width: 100%;height: 2000px;">打开控制台,滚动页面 查看效果</div>
<script>
function debounce(func, ms = 1000) {
let timer;
return function(...args) {
if(timer) {
clearTimeout(timer)
}
let runNow = !timer
timer = setTimeout(() => {
timer = null
}, ms)
if(runNow) func.apply(this, args)
}
}
const task = value => {
console.log(`防抖 立即执行:${value}`)
}
const handleScroll = debounce(task, 1000)
document.addEventListener('scroll', () => {
handleScroll('参数')
})
</script>
</body>
</html>
3. 立即执行与非立即执行结合版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>防抖(结合版-promise处理返回值)</title>
</head>
<body>
<div style="width: 100%;height: 2000px;">打开控制台,滚动页面 查看效果</div>
<script>
function debounce(func, ms = 1000, immediate = false) {
let timer, result
let debounced = function(...args) {
return new Promise(resolve => {
if(timer) {
clearTimeout(timer)
}
if(immediate) {
let runNow = !timer
timer = setTimeout(() => {
timer = null
}, ms)
if(runNow) {
result = func.apply(this, args)
resolve(result)
}
} else {
timer = setTimeout(() => {
result = func.apply(this, args)
resolve(result)
}, ms)
}
})
}
// 取消
debounced.cancel = function() {
clearTimeout(timer)
timer = null
}
return debounced
}
const task = num => {
console.log('执行task函数')
return num / 2
}
const handleScroll = debounce(task)
document.addEventListener('scroll', () => {
handleScroll(8).then(val => {
console.log(`执行结果是:${val}`)
})
})
</script>
</body>
</html>
二、节流
函数节流是指在高频事件触发期间,n秒内函数只会执行一次。
使用场景:click、scroll等
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>节流</title>
</head>
<body>
<div style="height: 1000px;width: 100%;">
打开控制台 滚动页面 开始测试节流函数吧
</div>
<script>
// 节流函数
function throttle (func, ms = 1000) {
// 通过闭包保存一个标记
let canRun = true
return function (...args) {
// 在函数开头判断标志是否为true,不为true则中断函数
if (!canRun) return
// 将canRun设置为false,防止执行之前再被执行
canRun = false
setTimeout(() => {
func.apply(this, args)
// 执行完事件之后,重新将这个标志设置为true
canRun = true
}, ms)
}
}
// 有些需要节流的工作 在这里执行
const task = () => {
console.log('执行了 节流')
}
const handlerScroll = throttle(task)
document.addEventListener('scroll', handlerScroll)
</script>
</body>
</html>