js防抖-节流函数的基本实现和补充详解
既白biu 人气:0前言
防抖和节流函数是我们在平常开发的时候,比较常用的两个工具函数,使用防抖和节流函数可以提高我们的效率,减小事件触发的频率,可以起到降低服务器压力的作用。并且在面试时候,这两个函数也是会经常会被问到,因此,了解防抖和节流函数的基本原理和应用场景,并且手写实现这两个函数至关重要
防抖函数
概念和应用场景
防抖函数:当事件触发的时候,并不会立刻执行,而是会等待一段事件,当事件密集触发时候,函数的触发会频繁的被推迟,只有等待了一段时间也没有事件触发,才会真正的执行。如下图所示:
防抖的应用场景很多:
- 输入框中频繁的输入内容,搜索或者提交信息;
- 频繁的点击按钮,触发某个事件;
- 监听浏览器滚动事件,完成某些特定操作;
- 用户缩放浏览器的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 }
加载全部内容