解决当函数在某些场景下被无限制的频繁调用,会增加浏览器的负担,会造成卡顿的现象
鼠标滚动、键盘输入操作、窗口resize等等
防抖:事件持续触发时,如果间隔时间小于预设的时间,不会执行函数,同时以最后触发事件的时间为准重新计时。总之,一定要在事件触发完的预设时间内没有触发过,才会执行函数
手写+逐步完善
function _debounce(func, wait){
// 定义一个计时器
var timer
return function(){
// 拿到当前的执行上下文
var context = this
clearTimeout(timer)
timer = setTimeout(function(){
// 因为setTimeout回调方法中this永远是window,所以~
func.apply(context)
},wait)
}
}
以上实现方式有一个不足,就是当事件处理函数传入参数(event对象)时,没有做到获取,修改一下:
function _debounce(func, wait){
var timer
return function(){
var context = this
// 通过arguments拿到调用时所传入的参数
var arg = arguments
clearTimeout(timer)
timer = setTimeout(function(){
// 执行时传入参数
func.apply(context, arg)
},wait)
}
}
此时还有一个问题,最开始函数必须要在事件初次触发后,等待wait之后才会执行,我们想要用一次立即执行来兜底初次触发:
function _debounce(func, wait, immediate){
var timer
return function(){
var context = this
var arg = arguments
if(timer) clearTimeout(timer)
if(immediate){
/** timer为true,callNow为false期间
* 都代表不是初次触发
* timer初始布尔值为false
* 初次执行之后timer在wait时间内一直为true
**/
var callNow = !timer
// 这一句在执行完之前 timer都为true
timer = setTimeout(function(){
timer = null
},wait)
if(callNow) func.apply(context,arg)
}else{
timer = setTimeout(function(){
func.apply(context, arg)
},wait)
}
}
}
// 中心思想就是有一个标识,用来标记当前是否为初次触发,用以判断是否需要执行,而在执行后wait时间后将标识变为初始状态
**特别版本:防抖函数有可能会造成一直不触发的现象
function throttle(fn, delay){
/*last为上一次触发回调的时间, timer是定时器*/
var last = 0, timer = null
return function () {
let context = this
let args = arguments
let now = +new Date()
/*判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值*/
if (now - last < delay) {
/* 如果时间间隔小于我们设定的时间间隔阈值,
* 则为本次触发操作设立一个新的定时器
* 其实这里就是原本正常的防抖逻辑
*/
clearTimeout(timer)
timer = setTimeout(function () {
last = now
fn.apply(context, args)
}, delay)
} else {
/* 如果时间间隔超出了我们设定的时间间隔阈值,那就不等了,无论如何要反馈给用户一次响应*/
last = now
fn.apply(context, args)
}
}
}
还可以有一些优化,比如有返回值时等等,暂时到这里 最好能记牢
节流:事件持续触发期间,每间隔预设的时间才会执行一次
手写+逐步完善(定时器 or 时间戳)
1、时间戳版本
function _throttle(func, wait){
var timer = 0
return function(){
var arg = arguments
var now = new Date()
if(now - timer >= wait){
func.apply(this,arg)
timer = now
}
}
}
/** 这里懵逼了,理解一下应该是将throttle执行后的结果,
* 也就是闭包函数赋值给了container的mousemove
* 之后每次mousemove执行的都是闭包函数
**/
container.onmousemove = throttle(xxx,1000)
2、定时器版本
function _throttle(func,wait){
var timer
return function(){
var arg = arguments
if(!timer){
timer = setTimeout(function(){
timer = null
func.apply(this,arg)
},wait)
}
}
}
以上两种实现方式,时间戳版本会立即执行,但不会有最后一一次执行;定时器版本不会立即执行,但会有最后一次操作执行兜底
最好是将这两种情况合二为一,更加优秀
3、取长补短版本:
function throttle(func, wait) {
var timeout, context, args, result;
var previous = 0;
var later = function() {
previous = +new Date();
timeout = null;
func.apply(context, args)
};
var throttled = function() {
var now = +new Date();
//下次触发 func 剩余的时间
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);
} else if (!timeout) {
// 如果时间戳还没有到执行,定时器到了(没有定时器代表要执行了),那就在剩余时间后执行,兜最后一次
timeout = setTimeout(later, remaining);
}
};
return throttled;
}
// 这个难 暂时先理解吧
是在写keyup事件的时候,每次触发,都会请求后台接口,为了避免,每次请求,键盘弹起之后,隔上一段时间再去请求,所以用防抖函数,多次事件触发后、事件处理函数只执行一次
在 js 中 改变窗口大小 & 上下滚动滚动条 & 反复向输入框中输入内容 ... , 如果绑定了相应的事件 , 这些事件的触发频率非常高, 严重影响用户体验和服务器的性能 , 这种问题 在js中 就叫 js 的抖动 .
防抖和节流严格算起来应该属于性能优化的知识,但实际上遇到的频率相当高,处理不当或者放任不管就容易引起浏览器卡死。所以还是很有必要早点掌握的。(信我,你看完肯定就懂了)
有些浏览器事件可以在短时间内快速触发多次,比如调整窗口大小或向下滚动页面。例如,监听页面窗口滚动事件,并且用户持续快速地向下滚动页面,那么滚动事件可能在 3 秒内触发数千次,这可能会导致一些严重的性能问题
应用场景:多次点击提交按钮 首次提交执行,重复提交就会等待一定的时间提交执行;首次点击提交按钮会立即执行一次debounce方法,后面3s内不触发事件才能继续执行
面试的时候我们经常会问别人是理解什么是节流和防抖,严格的可能要求你写出节流和防抖函数,这里我们抛开loadsh工具库手写节流和防抖
函数防抖的理解:连续触发事件,最后一次事件触发后,超过了规定时间还没有再次触发,这时候会执行一次事件,这就是函数的防抖
防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。节流:规定在一个单位时间内,只能触发一次函数。
防抖,顾名思义,防止抖动,以免把一次事件误认为多次,敲键盘就是一个每天都会接触到的防抖操作。想要了解一个概念,必先了解概念所应用的场景。在 JS 这个世界中,有哪些防抖的场景呢
防抖又叫为函数防抖(debounce):指触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间。前端开发中,常见的事件如,onresize,scroll,mousemove ,mousehover
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!