前言
防抖和节流对大家来说应该都不陌生,不仅是在面试中还是在实际开发中,经常会遇到防抖和节流的问题。对于很多初学的小伙伴,可能对于这两者的概念的都没有区分清楚,更不知道在哪些应用场景下用防抖还是节流。所以,本文主要带着大家来区分这两者的区别、具体的应用场景以及手写这两个函数。
防抖
场景
我们都遇到过这样的场景,在某个搜索框中输入自己想要搜索的内容
比如想要输入一个 javascript,
当输入第一字符j时,为了更好的用户体验,通常会出现对应的联想内容,这些联想内容通常是保存在服务器的 一次网络请求;
当输入ja时,再次发送网络请求,那么输入javascript一共需要10次网络请求,大大损耗我们整个系统的性能,无论是前端的事件处理,还是对于服务器的压力;
那么,我们只需要在合适的情况下发送网络请求。比如用户快速的输入了javascript,那么只发送一次网络请求,或者如果用户是输入一个j想了一会儿,这个时候j确实应该发送一次网络请求;
也就是我们应该监听用户在某个时间,比如500ms内,没有再次触发时间时,再发送网络请求;
定义
防抖的定义:只有在某个时间内,没有再次触发某个函数时,才真正的调用这个函数
当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间;
当事件密集触发时,函数的触发会被频繁的推迟;
只有等待了一段时间也没有事件触发,才会真正的执行响应函数;
节流
场景
很多人都玩过飞机大战的游戏,在飞机大战的游戏中,我们按下空格会发射一个子弹:
很多飞机大战的游戏中会有这样的设定,即使按下的频率非常快,子弹也会保持一定的频率来发射;
比如1秒钟只能发射一次,即使用户在这1秒钟按下了10次,子弹会保持发射一颗的频率来发射;
但是事件是触发了10次的,响应的函数只触发了一次;
定义
节流的定义:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
当事件触发时,会执行这个事件的响应函数;
如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函数;
不管在这个中间有多少次触发这个事件,执行函数的频繁总是固定的;
实现
1.直接使用第三方工具库: underscore/lodash;
underscore地址:underscorejs.org/
2.手写防抖函数
2.1 基本的防抖函数
index.html
<script src="./debounce.js"></script>
<input type="text">
<script>
const input = document.querySelector("input")
let couter = 0;
const inputChange = function() {
console.log(`发送了第${++couter}次请求`);
}
input.oninput = debounce(inputChange, 2000)
</script>
复制代码
debounce.js
function debounce(fn, delay) {
//1.定义一个定时器变量,来保存上次的定时器
let timer = null
// 2.真正执行的函数
const _debounce = function () {
// 3.如果定时器存在,取消上一次的定时器
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
// 外部传入的的函数
fn()
},delay)
}
return _debounce
}
复制代码
2.2 this和...agrs
1.这里不能直接写的fn()
,因为此时的this
是指向window
的,所以必须通过fn.apply()
来改变this的指向,才能是this
指向input
对象。
2.如果有参数,利用...agrs
来接收。
function debounce(fn, delay) {
let timer = null
const _debounce = function (...args) { // ...args用来接收参数
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args) // 利用apply改变this的指向input
},delay)
}
return _debounce
}
复制代码
2.3 立即执行
定义: 在第一次输入的时候,先立即发送一次网络请求,后面的输入就是正常的防抖。
function debounce(fn, delay, immediate = false) {
let timer = null
let isInvoke = false // 设置一个新的自由变量来控制
const _debounce = function (...args) {
if(timer) clearTimeout(timer)
// 判断是否立即执行
if(immediate && !isInvoke) {
fn.apply(this, args)
isInvoke = true
} else {
timer = setTimeout(() => {
fn.apply(this, args)
isInvoke = false
},delay)
}
}
return _debounce
}
复制代码
2.4:取消功能
定义: 当用户输入输入到最后时(请求还没发送),点击了取消按钮(相当于取消了发送)
const debounceChange = debounce(inputChange, 3000)
input.oninput = debounceChange
// 取消功能
const cancelBtn = document.quserySelector('#cancel')
cancelBtn.onclick = function() {
debounceChange.cancel()
}
function debounce(fn, delay) {
let timer = null
const _debounce = function () {
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
fn()
},delay)
// 封装取消功能
_debounce.cancle = function(){
if(timer) clearTimeout(timer) // 清除最后的定时器
}
}
_
return _debounce
}
复制代码
3.手写节流函数
节流比防抖稍微复杂一些,首要我们来理解一下节流的逻辑过程:
3.1 基本的节流函数
function throttle(fn, interval) {
let lastTime = 0
const _throttle = function() {
const nowTime = new Date().getTime();
const remainTime = interval - (nowTime - lastTime);
// 执行函数
if(remainTime <= 0) {
fn();
lastTime = nowTime
}
}
return _throttle
}
复制代码
应用场景
防抖
1.输入框中频繁的输入内容,搜索或者提交信息;
2.频繁的点击按钮,触发某个事件;
3.监听浏览器滚动事件,完成某些特
4.用户缩放浏览器的resize事件;
节流
1.监听页面的滚动事件
2.鼠标移动事件
3.用户频繁点击按钮操作
4.游戏中的一些设计