Javascript 防抖/节流
问题
Javascript 防抖/节流
答案
防抖 在滚动事件中需要做个复杂计算或者实现⼀个按钮的防⼆次点击操作。可以通 过函数防抖动来实现
// 使⽤ underscore 的源码来解释防抖动
/**
* underscore 防抖函数,返回函数连续调⽤时,空闲时间必须⼤于或等于 wait,func 才会执⾏
*
* @param {function} func 回调函数
* @param {number} wait 表示时间窗⼝的间隔
* @param {boolean} immediate 设置为ture时,是否⽴即调⽤函数
* @return {function} 返回客户调⽤函数
*/
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
var later = function() {
// 现在和上⼀次时间戳⽐较
var last = _.now() - timestamp;
// 如果当前间隔时间少于设定时间且⼤于0就重新设置定时器
if (last < wait && last >= 0) {
timeout = setTimeout(later, wait - last);
} else {
// 否则的话就是时间到了执⾏回调函数
timeout = null;
if (!immediate) {
result = func.apply(context, args);
if (!timeout) context = args = null;
}
}
};
return function() {
context = this;
args = arguments;
// 获得时间戳
timestamp = _.now();
// 如果定时器不存在且⽴即执⾏函数
var callNow = immediate && !timeout;
// 如果定时器不存在就创建⼀个
if (!timeout) timeout = setTimeout(later, wait);
if (callNow) {
// 如果需要⽴即执⾏函数的话 通过 apply 执⾏
result = func.apply(context, args);
context = args = null;
}
return result;
};
};
整体函数实现
对于按钮防点击来说的实现
- 开始⼀个定时器,只要我定时器还在,不管你怎么点击都不会执⾏回调函数。⼀旦定时器结束并设置为 null,就可以再次点击了
- 对于延时执⾏函数来说的实现:每次调⽤防抖动函数都会判断本次调⽤和之前的时间间隔,如果⼩于需要的时间间隔,就会重新创建⼀个定时器,并且定时器的延时为设定时间减去之前的时间间隔。⼀旦时间到了,就会执⾏相应的回调函数
节流
防抖动和节流本质是不⼀样的。防抖动是将多次执⾏变为最后⼀次执⾏,节流 是将多次执⾏变成每隔⼀段时间执⾏
/**
* underscore 节流函数,返回函数连续调⽤时,func 执⾏频率限定为 次 / wait
*
* @param {function} func 回调函数
* @param {number} wait 表示时间窗⼝的间隔
* @param {object} options 如果想忽略开始函数的的调⽤,传⼊{leading: false
* 如果想忽略结尾函数的调⽤,传⼊{trailing: false
* 两者不能共存,否则函数不能执⾏
* @return {function} 返回客户调⽤函数
*/
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
// 之前的时间戳
var previous = 0;
// 如果 options 没传则设为空对象
if (!options) options = {};
// 定时器回调函数
var later = function() {
// 如果设置了 leading,就将 previous 设为 0
// ⽤于下⾯函数的第⼀个 if 判断
previous = options.leading === false ? 0 : _.now();
// 置空⼀是为了防⽌内存泄漏,⼆是为了下⾯的定时器判断
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
// 获得当前时间戳
var now = _.now();
// ⾸次进⼊前者肯定为 true
// 如果需要第⼀次不执⾏函数
// 就将上次时间戳设为当前的
// 这样在接下来计算 remaining 的值时会⼤于0
if (!previous && options.leading === false) previous = now;
// 计算剩余时间
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 如果当前调⽤已经⼤于上次调⽤时间 + wait
// 或者⽤户⼿动调了时间
// 如果设置了 trailing,只会进⼊这个条件
// 如果没有设置 leading,那么第⼀次会进⼊这个条件
// 还有⼀点,你可能会觉得开启了定时器那么应该不会进⼊这个 if 条件了
// 其实还是会进⼊的,因为定时器的延时
// 并不是准确的时间,很可能你设置了2秒
// 但是他需要2.2秒才触发,这时候就会进⼊这个条件
if (remaining <= 0 || remaining > wait) {
// 如果存在定时器就清理掉否则会调⽤⼆次回调
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
// 判断是否设置了定时器和 trailing
// 没有的话就开启⼀个定时器
// 并且不能不能同时设置 leading 和 trailing
timeout = setTimeout(later, remaining);
}
return result;
};
};
更多面试题
如果你想了解更多的前端面试题,可以查看本站的WEB前端面试题 ,这里基本包涵了市场上的所有前端方面的面试题,也有一些大公司的面试图,可以让你面试更加顺利。
面试题 | ||
---|---|---|
HTML | CSS | JavaScript |
jQuery | Vue.js | React |
算法 | HTTP | Babel |
BootStrap | Electron | Gulp |
Node.js | 前端经验相关 | 前端综合 |
Webpack | 微信小程序 | - |
这些题库还在更新中,如果你有不错的面试题库欢迎分享给我,我整理后放上来;人人为我,我为人人,互帮互助,共同提高,祝大家都拿到心仪的Offer!