js去抖和节流函数详解
Posted vcxiaohan2
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js去抖和节流函数详解相关的知识,希望对你有一定的参考价值。
Debounce 和 Throttle 的原理及实现仅作为理解去抖和节流函数的概念,里面提供的代码并不一定正确
示例:
- 网上做示例,一般用scroll或者mousemove事件,我们很难控制触发事件的次数,这里我们使用click事件,我们触发了几次事件,心里都有数,更便于个人对去抖和节流函数的理解
- 以下去抖和节流函数代码均为自己理解所写,功能并不全面,但是一般的场景均能满足
- 去抖和节流函数均没有考虑返回值的情况
- 节流函数没有考虑立即执行和延时执行同时存在的情况
- 截图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>demo</title>
<style>
*
margin: 0;
padding: 0;
body,
html
width: 100%;
height: 100%;
div
float: left;
width: 250px;
height: 150px;
border: 1px solid red;
cursor: pointer;
</style>
</head>
<body>
<p>分别点击以下区域,并观察控制栏打印结果</p>
<div class="a">
<p>debounce方法去抖,延时执行</p>
<h3>应用:</h3>
<p>1 缩放浏览器</p>
<p>2 键盘输入文字自动提示</p>
<p>3 滚动到底部加载下一页</p>
</div>
<div class="b">
<p>debounce方法去抖,立即执行</p>
<h3>应用:</h3>
<p>1 防止用户短时间内多次提交</p>
</div>
<div class="c">
<p>throttle方法节流,延时执行</p>
<h3>应用:</h3>
<p>1 缩放浏览器</p>
</div>
<div class="d">
<p>throttle方法节流,立即执行</p>
<h3>应用:</h3>
<p>1 滚动时全屏切换,如:<a href="http://www.jq22.com/yanshi1124" target="_blank">jQuery全屏滚动插件fullPage.js演示</a></p>
</div>
</body>
<script>
let a = debounce(function (e)
console.log('debounce方法去抖,延时执行', e.target, `事件类型:$e.type`)
, 500)
let b = debounce(function (e)
console.log('debounce方法去抖,立即执行', e.target, `事件类型:$e.type`)
, 500, true)
let c = throttle(function (e)
console.log('throttle方法节流,延时执行', e.target, `事件类型:$e.type`)
, 500)
let d = throttle(function (e)
console.log('throttle方法节流,立即执行', e.target, `事件类型:$e.type`)
, 500, true)
// 绑定滚动事件
document.querySelector('.a').addEventListener('click', a)
document.querySelector('.b').addEventListener('click', b)
document.querySelector('.c').addEventListener('click', c)
document.querySelector('.d').addEventListener('click', d)
// 去抖(多次事件只执行1次,wait为延时执行时间,immediate表示首次事件是立即执行还是延时执行)
function debounce(fn, wait, immediate)
// 闭包形成局部作用域
// 定时器
let timer = null
return function () // #1
// 保存#1的this上下文
const self = this
// 保存#1的参数列表
const args = arguments
// 清除定时器,阻止fn的执行
clearTimeout(timer)
if (immediate) // 事件立即执行
// 首次事件时,timer为null
let callNow = !timer
// 接下来的事件,timer都是定时器的引用,即存在
timer = setTimeout(() =>
// 延时时间过后,恢复timer为null
timer = null
, wait)
// 首次事件时,callNow为true,所以会立即执行
if (callNow) fn.apply(self, args)
else // 事件延时执行
// 开启新的定时器,延时wait时间后,执行fn
timer = setTimeout(() =>
// 执行fn,改变this指向#1的上下文,并传入#1的参数
fn.apply(self, args)
, wait)
// 节流(多次事件间隔执行,wait为间隔执行时间,immediate表示首次事件是立即执行还是延时执行)
function throttle(fn, wait, immediate)
// 闭包形成局部作用域
// 定时器
let timer = null
// 开始时间
let start = 0
return function () // #1
// 保存#1的this上下文
const self = this
// 保存#1的参数列表
const args = arguments
if (immediate) // 立即执行
// 每次要执行fn时,记录当前时间(此时fn并未执行)
let end = new Date()
if (end - start >= wait) // 累积时间大于间隔时间时执行(首次事件的时间戳远远大于wait)
// 执行fn,改变this指向#1的上下文,并传入#1的参数
fn.apply(self, args)
// 更新开始时间
start = end
else // 延时执行
if (!timer)
timer = setTimeout(() =>
// 执行fn,改变this指向#1的上下文,并传入#1的参数
fn.apply(self, args)
// 赋值为null,以便下次继续执行
timer = null
, wait)
</script>
</html>
网上更完美的封装函数
// 去抖(默认延迟执行,通过immediate来开启相应的效果)
/* // 延迟执行
debounce(function () , 500)
// 立即执行
debounce(function () , 500, true) */
function debounce(func, wait, immediate)
var timeout, result
return function ()
var context = this
var args = arguments
if (timeout) clearTimeout(timeout)
if (immediate)
var callNow = !timeout
timeout = setTimeout(function ()
timeout = null
, wait)
if (callNow) result = func.apply(context, args)
else
timeout = setTimeout(function ()
func.apply(context, args)
, wait)
return result
// 节流(默认立即执行和延迟执行同时开启,通过options来关闭相应的效果,但是两者不能同时关闭)
/* // 立即执行和延迟执行同时开启
throttle(function () , 500)
throttle(function () , 500,
// 取消立即执行
leading: false
)
throttle(function () , 500,
// 取消延迟执行
trailing: false
) */
function throttle(func, wait, options)
var timeout, context, args, result
var previous = 0
if (!options) options =
var later = function ()
previous = options.leading === false ? 0 : new Date().getTime()
timeout = null
func.apply(context, args)
if (!timeout) context = args = null
var throttled = function ()
var now = new Date().getTime()
if (!previous && options.leading === false) previous = now
var remaining = wait - (now - previous)
context = this
args = arguments
if (remaining <= 0 || remaining > wait)
if (timeout)
clearTimeout(timeout)
timeout = null
previous = now
func.apply(context, args)
if (!timeout) context = args = null
else if (!timeout && options.trailing !== false)
timeout = setTimeout(later, remaining)
return throttled
以上是关于js去抖和节流函数详解的主要内容,如果未能解决你的问题,请参考以下文章