js函数的防抖(debounce)与节流(throttle)-测试案例
公共方法
const btn = document.getElementById("btn");
const btn2 = document.getElementById("btn2");
const btn3 = document.getElementById("btn3");
function Money(){
console.log("点击了按钮",this);
}
函数防抖-debounce
测试demo-看控制台
页面结构
<button id="btn">非立即执行版 按钮</button>
<button id="btn2">非立即执行版 按钮</button>
<button id="btn3">合成版 按钮</button>
短时间内多次触发同一事件,只执行最后一次,或者只执行最开始的一次,中间的不执行
非立即执行版的意思是触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间
// 非立即执行版
function debounce(fun,delay){
let timer;
return function (){
let context = this; // 注意 this 指向
let args = arguments; // arguments中存着e
// 清除定时器
if (timer) clearTimeout(timer);
timer = setTimeout(()=>{
// 绑定this
// fun.call(context,args);
fun.apply(this, args);
},delay);
}
}
立即执行版的意思是触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果
// 立即执行版
function debounces(fun, wait) {
let timer;
return function() {
let context = this; // 这边的 this 指向谁?
let args = arguments; // arguments中存着e
if (timer) clearTimeout(timer);
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait)
if (callNow) fun.apply(context, args);
}
}
合成版
// 合成版
/**
* @desc 函数防抖
* @param fun 目标函数
* @param wait 延迟执行毫秒数
* @param immediate true - 立即执行, false - 延迟执行
*/
function debouncehc(fun, wait, immediate) {
let timer;
return function() {
let context = this,
args = arguments;
if (timer) clearTimeout(timer);
if (immediate) {
let callNow = !timer;
timer = setTimeout(() => {
timer = null;
}, wait);
if (callNow) fun.apply(context, args);
} else {
timer = setTimeout(() => {
fun.apply(context, args);
}, wait)
}
}
}
使用
btn.addEventListener("click",debounce(Money,1000));
btn2.addEventListener("click",debounces(Money,1000));
btn3.addEventListener("click",debouncehc(Money,1000,false));
节流-throttle
测试demo-看控制台
指连续触发事件但是在 n 秒中只执行一次函数。即 2n 秒内执行 2 次... 。节流如字面意思,会稀释函数的执行频率
页面结构
<button id="btn">时间戳版 按钮</button>
<button id="btn2">定时器版 按钮</button>
<button id="btn3">合成版 按钮</button>
时间戳版
持续触发事件的过程中,函数会立即执行,并且每 1s 执行一次
// 时间戳版
function throttle(fun, wait) {
let previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > wait) {
fun.apply(context, args);
previous = now;
}
}
}
定时器版
持续触发事件的过程中,函数不会立即执行,并且每 1s 执行一次,在停止触发事件后,函数还会再执行一次
// 定时器版
function throttles(fun, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
fun.apply(context, args)
}, wait)
}
}
}
区别就是,时间戳版的函数触发是在时间段内开始的时候,而定时器版的函数触发是在时间段内结束的时候。
合并版
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 表时间戳版,2 表定时器版
*/
function throttlehc(func, wait, type) {
let previous;
let timeout;
if (type === 1) {
previous = 0;
} else if (type === 2) {
timeout;
}
return function() {
let context = this;
let args = arguments;
if (type === 1) {
let now = Date.now();
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
} else if (type === 2) {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}
使用
btn.addEventListener("click",throttle(Money,1000));
btn2.addEventListener("click",throttles(Money,1000));
btn3.addEventListener("click",throttlehc(Money,1000,2));
关于节流/防抖函数中 context(this) 的指向解析
首先,在执行 throttle(count, 1000) 这行代码的时候,会有一个返回值,这个返回值是一个新的匿名函数,因此 content.onmousemove = throttle(count,1000); 这句话最终可以这样理解
content.onmousemove = function() {
let now = Date.now();
let context = this;
let args = arguments;
...
console.log(this)
}
到这边为止,只是绑定了事件函数,还没有真正执行,而 this 的具体指向需要到真正运行时才能够确定下来。
所以这个时候如果我们把前面的 content.onmousemove 替换成 var fn 并执行 fn fn() ,此时内部的 this 打印出来就会是 window 对象。
其次,当我们触发 onmousemove 事件的时候,才真正执行了上述的匿名函数,即 content.onmousemove() 。
此时,上述的匿名函数的执行是通过 对象.函数名() 来完成的,那么函数内部的 this 自然指向 对象。
最后,匿名函数内部的 func 的调用方式如果是最普通的直接执行 func() ,那么 func 内部的 this 必然指向 window ,虽然在代码简单的情况下看不出什么异常(结果表现和正常一样),但是这将会是一个隐藏 bug,不得不注意啊!
所以,我们通过匿名函数捕获 this,然后通过 func.apply() 的方式,来达到 content.onmousemove = func 这样的效果。
可以说,高阶函数内部都要注意 this 的绑定。
本文来自博客园,作者:JackieDYH,转载请注明原文链接:https://www.cnblogs.com/JackieDYH/p/17634303.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现