前言
本文介绍了js中常见的2中函数优化机制,函数防抖与节流,他们都是用来防止函数被频繁调用的,但使用的场景略有差别。
防抖
我们在访问百度、谷歌、淘宝等网站的时候经常遇到边输入边提示的情况,网站会根据我们输入的关键词推测我们想要搜索的内容,提升用户体验。如下所示:
提示信息
我们来考虑一下如何实现这个功能。首先我们需要监听输入框的内容变化,当内容变化的时候需要向服务端发送网络请求获取所示提示内容,如下代码所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>函数防抖</title>
</head>
<body>
<input id='input'/>
<script>
const input = document.getElementById('input')
let timerId = null
input.oninput = function (event) {
console.log(event.srcElement.value)
}
</script>
</body>
</html>
通过oninput
函数我们可以拿到用户的输入值,然后去发送网络请求。这里有一个坑需要我们注意,因为每次用户输入都会触发oninput方法,假设平均一次网络请求所需要的时间是1s,假如1s内用户输入3个字符ABC,如果输入框的值一变化我们就发送网络请求,那么就会发送三个网络请求,关键字分别为A
,AB
,ABC
但,服务端网络平均耗时为1s,也就是说 你输入ABC的时候,第一个和网络请求才返回,但这时用户已经输入到ABC
了,这时候再显示A
或者B
的推荐结果显示不合适。
怎么办?我们应该避免频繁的请求,一方面是为了减轻服务端的压力,一方面是为了得到更好的用户前端体验,我们需要限制网络请求频率,这可以通过定时器来完成,这就是函数防抖:通过定时器来减少不必要的函数的调用频率,以便得到更好的性能和用户体验。
let timerId = null
input.oninput = function (event) {
if (timerId) {
clearTimeout(timerId)
}
timerId = setTimeout(() => {
console.log(event.srcElement.value)
}, 1000);
}
上面代码中,我们首先声明了一个定时器变量,当触发输入框的值变化的时候,我们先判断定时器是否有值,如果有值就取消它,并重新给他赋值。它表达的意思是:当用户停止输入1s后再去处理网络请求,帮助用户提示。
我们可以将它封装成固定的防抖函数debounce
, 它接收两个参数,第一个参数fn:表示需要执行的任务,第二个参数delay: 表示定时器的延迟时间。
function debounce(fn, delay) {
let timerId = null
let self = this
return function() {
if (timerId) {
clearTimeout(timerId)
}
let args = arguments
timerId = setTimeout(() => {
fn.apply(self, args)
}, time);
}
}
其中值得注意的是:1. 需要fn需要使用apply进行this的绑定,保证其与debounce
执行的环境一样。2. 需要使用arguments 将外传出事件参数。
之后我们可以修改oninput事件,如下:
function test(event) {
console.log(event)
}
input.oninput = debounce(test, 1000)
节流
节流与防抖类似,防抖的特点是:指定的行为停止指定的时间之后,再执行对应的任务。但是有些场景下我们需要在中间状态执行对应的任务,比如如下场景:我们在浏览器的可视区域中心放一个长方形,当改变浏览器的可视区域大小后,长方形的大小也跟随改变,始终保持长方形的大小为可视区域的1/4。如果我们用下面的防抖函数实现,长方形只会在停止拖动的时候改变大小,显的比较突兀,我们希望在拖动的过程中也能看到长方形大小的变化。
const input = document.getElementById('root')
function debounce(fn, time) {
let timerId = null
let self = this
return function() {
if (timerId) {
clearTimeout(timerId)
}
let args = arguments
timerId = setTimeout(() => {
fn.apply(self, args)
}, time);
}
}
function reSetSize() {
let width = window.innerWidth
let height = window.innerHeight
input.style.width = width/2 + 'px'
input.style.height = height/2 + 'px'
}
reSetSize()
window.onresize = debounce(reSetSize, 1000);
我们可以对防抖函数进行改造,让它变成节流函数,通过增加一个flag标识,这个函数就变成了每n时长执行对应的操作。
function throttle(fn, time) {
let flag = true
let timerId = null
let self = this
return function() {
if (!flag) {
return
}
flag = false
if (timerId) {
clearTimeout(timerId)
}
let args = arguments
timerId = setTimeout(() => {
flag = true
fn.apply(self, args)
}, time);
}
}
改变onresize
的监听,就可以看到边拖动边改变长方形的效果了。
window.onresize = debounce(reSetSize, 1000);
总结
通过本文,你了解到了函数的防抖与节流,他们的区别是:
防抖
当某一行为停止之后过n时长再执行响应的操作。节流
每个n时长就执行一次函数。
推荐阅读
点赞,关注 是你对我最大的鼓励。
更多优质内容,请扫码关注公众号:RiverLi。