前端值得一探究竟特辑 --- 防抖与节流
防抖
防抖函数的作用就是控制函数在一定时间内的执行次数。防抖意味着N秒内函数只会被执行一次,如果N秒内再次被触发,则重新计算延迟时间。
以乘车刷卡的情景举例,只要乘客不断地在刷卡,司机师傅就不能开车,乘客刷卡完毕之后,司机会等待几分钟,确定乘客坐稳再开车。如果司机在最后等待的时间内又有新的乘客上车,那么司机等乘客刷卡完毕之后,还要再等待一会,等待所有乘客坐稳再开车。
1. 防抖的应用
- 搜索框输入即查询,如果用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户停止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
- 浏览器窗口缩放,resize事件(如窗口停止改变大小之后重新计算布局)等。
2. 防抖的实现
思路:每次触发事件时都取消之前的延时调用方法。
// HTML
<input id="dom" />
// JS
var dom = document.getElementById("dom");
dom.addEventListener("input", debounce(() => {
console.log("假装我是一个请求FUNC");
}, 1000));
function debounce(fn, wait) {
var time = null;
return function () {
clearTimeout(time);
time = setTimeout(() => {
fn.apply(this, arguments)
}, wait);
}
}
陷阱:确保debounce函数的this(上下文还是div)
setTimeout第一个参数使用箭头函数且手动将this绑定到fn上,确保fn的上下文是div,缺一不可,否则的话 this为window和直接 fn()调用是一样的效果,因为他们的this都是window,如果再fn内部要使用事件对象,是获取不到的。
time = setTimeout(() => {
console.log(this); //div
fn.apply(this, arguments)//确保fn函数的this(上下文还是div),所有setTimeOut第一个参数使用箭头函数而不是普通函数,确保this是div,并手动将this绑定到fn上
}, wait);
3. 详解执行过程
首先,页面加载,代码解析到脚本部分
-
函数和变量声明提升
-
代码自上而下执行
dom.addEventListener("input", debounce(() => { console.log("假装我是一个请求FUNC"); }, 1000));
这里会调用debounce, 执行环境为debounce开辟一个栈内存,最终addEventListener第二个参数(回调函数)存储的是堆地址H10087。由于回调函数中占用了time、fn、wait,造成了栈内存S10086执行完毕却无法进行内存回收,形成闭包。
-
事件触发,addEventListener调用回调函数,执行环境为回调函数开辟一个栈内存S1008701,在代码执行过程中,又调用了setTimeOut,环境为setTimeOut开辟一个栈内存S100870101,setTimeOut第一个参数(回调函数)存储的是堆地址H1008702。此时栈内存S10086中wait依旧是1000,随时间递减的是setTimeOut函数内部的形参。
-
若wait还没有置0,setTimeOut第一个参数(回调函数堆内存H1008702)还没有被触发,事件再次触发,addEventListener再次调用回调函数(堆内存 H10087)。
clearTimeout(time);
的time是栈内存栈 S10086/ debounce执行中的time,存储着上一轮回调函数执行的setTimeOut手柄。在此上一轮的setTimeOut将被清除,time将存储此轮回调函数执行的setTimeOut手柄。
节流
节流的作用是规定一个单位时间,在这个单位时间内最多只能触发一次函数执行,如果这个单位时间内多次触发函数,只能有一次生效。每次触发事件时都判断当前是否有等待执行的延时函数
1. 节流的应用
- 按钮点击事件
- 拖拽事件
- onScoll
- 计算鼠标移动的距离(mousemove)
2. 防抖的实现
每次触发事件时都查询是否有等待执行的延时函数。
// HTML
<input id="dom" type="button" value="提交" />
// JS
var dom = document.getElementById("dom");
var i = 1;
dom.addEventListener("click", throttle((e) => {
console.log("假装我是一个请求FUNC");
console.log(Date());
}, 1000));
function throttle(fn, wait) {
var time = null;
var previous = 0;
return function () {
console.log('节流');
var now = new Date();
if (now - previous > wait) {
fn.apply(this, arguments);
previous = now;
}
}
}
无论我点击多少次提交,始终限定在1秒内只提交1次。
上一篇: JSP之弹出登陆框以及阴影效果
下一篇: 数据库手动设置数据约束