前言
防抖和节流函数是我们在平常开发的时候,比较常用的两个工具函数,使用防抖和节流函数可以提高我们的效率,减小事件触发的频率,可以起到降低服务器压力的作用。并且在面试时候,这两个函数也是会经常会被问到,因此,了解防抖和节流函数的基本原理和应用场景,并且手写实现这两个函数至关重要
防抖函数
概念和应用场景
防抖函数:当事件触发的时候,并不会立刻执行,而是会等待一段事件,当事件密集触发时候,函数的触发会频繁的被推迟,只有等待了一段时间也没有事件触发,才会真正的执行。如下图所示:
防抖的应用场景很多:
- 输入框中频繁的输入内容,搜索或者提交信息;
- 频繁的点击按钮,触发某个事件;
- 监听浏览器滚动事件,完成某些特定操作;
- 用户缩放浏览器的resize事件
案例:
在某个搜索框中输入自己想要搜索的内容:
比如想要搜索一个MacBook:
当我输入m时,为了更好的用户体验,通常会出现对应的联想内容,这些联想内容通常是保存在服务器的,所以需要一次网络请求;当继续输入ma时,再次发送网络请求;
那么macbook一共需要发送7次网络请求; 这大大损耗我们整个系统的性能,无论是前端的事件处理,还是对于服务器的压力;
但是我们需要这么多次的网络请求吗?
不需要,正确的做法应该是在合适的情况下再发送网络请求; 比如如果用户快速的输入一个macbook,那么只是发送一次网络请求;
比如如果用户是输入一个m想了一会儿,这个时候m确实应该发送一次网络请求;
也就是我们应该监听用户在某个时间,比如500ms内,没有再次触发时间时,再发送网络请求;
这就是防抖的操作:只有在某个时间内,没有再次触发某个函数时,才真正的调用这个函数;
手写实现防抖函数
那么如何通过代码实现防抖的功能呢?
首先,防抖函数应该传入一个需要实现防抖的函数,还有一个时间,该时间表示在规定时间内,如果再次触发此次事件,则取消上次事件。返回的结果应该是一个执行了此功能的函数。
那么整体的框架如下,只需要在_debounce函数里面实现功能即可。
function debounce(fn,delay){
const _debounce = function(){
}
return _debounce
}
函数的参数传进来一个时间,该时间表示在规定时间内,如果再次触发此次事件,则取消上次事件,所以需要有定时器来记录时间。并且有取消事件的过程,所以我们需要在满足条件时候,取消定时器,即clearTimeout,那么还有个问题,fn函数this指向。
如果在_debounce中单独调用传进来的函数fn,即以fn()形式直接调用,那么fn是属于独立的函数调用,this指向的是window,实际this指向的是调用函数的对象,所以会出现错误。
我们知道事件调用的时候,响应函数参数会传进来一个event事件对象,所以我们要对这个参数(event事件对象)做处理。
那么:
timer定义一个定时器,保存上一次的定时器,在_debounce函数里面,如果timer不为空,那么先清除上一次的定时器,之后再给timer赋值,让他延迟执行fn函数,fn函数用apply改变了this指向,在setTimeout内部使用的是箭头函数,由于箭头函数内部没有this,所以会去上层找this,那么这个this实际上就是调用debounce的对象。
比如输入框的输入事件做防抖处理:
const inputEl = document.querySelector(“input”)
inputChange是输入事件触发后所要执行的函数,可以给他做防抖处理:const debounceChange = debounce(inputChange,3000),然后执行: inputEl.oninput = debounceChange,如果不加防抖处理,毫无疑问,每次在输入框输入时候,都会触发事件,加了防抖处理后,3秒内多次调用,下一次事件会把上一次事件清除掉。
这里的this通过apply调用后,指向的就是inputEl这个元素,
function debounce(fn, delay) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null
// 2.真正执行的函数
const _debounce = function(...args) {
// 取消上一次的定时器
if (timer) clearTimeout(timer)
// 延迟执行
timer = setTimeout(() => {
// 外部传入的真正要执行的函数
fn.apply(this, args)
}, delay)
}
return _debounce
}
节流函数
概念和应用场景
节流函数:当事件执行时候,会执行这个事件的响应函数;如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函数;不管在这个中间有多少次触发这个事件,执行函数的频繁总是固定的;如图所示:
节流的应用场景:
- 监听页面的滚动事件;
- 鼠标移动事件;
- 用户频繁点击按钮操作;
- 游戏中的一些设计
节流的具体应用场景:
很多人都玩过类似于飞机大战的游戏
在飞机大战的游戏中,我们按下空格会发射一个子弹:
很多飞机大战的游戏中会有这样的设定,即使按下的频率非常快,子弹也会保持一定的频率来发射;
比如1秒钟只能发射一次,即使用户在这1秒钟按下了10次,子弹会保持发射一颗的频率来发射;
但是事件是触发了10次的,响应的函数只触发了一次
手写实现节流函数
那么如何通过代码实现防抖的功能呢?
首先,我们需要两个变量记录当前事件触发的时间和上一次的开始时间,函数的参数会传进来间隔的时间,由此我们可以计算出下次触发的最低间隔时间,即为间隔时间-(当前事件触发的时间-上次的开始时间),如果间隔时间<=0,那么执行fn函数,并且修改上一次的开始时间为当前事件触发时间。
function throttle(fn, interval) {
// 1.记录上一次的开始时间
let lastTime = 0
// 2.事件触发时, 真正执行的函数
const _throttle = function(...args) {
// 2.1.获取当前事件触发时的时间
const nowTime = new Date().getTime()
// 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长事件需要去触发函数
const remainTime = interval - (nowTime - lastTime)
if (remainTime <= 0) {
// 2.3.真正触发函数
fn.apply(this,args)
// 2.4.保留上次触发的时间
lastTime = nowTime
}
}
return _throttle
}
到此这篇关于js防抖-节流函数的基本实现和补充详解的文章就介绍到这了,更多相关js防抖节流函数的实现内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!