函数的防抖节流
前言
防抖(debounce) 和 节流(throttling):函数的防抖和节流目的都是为了限制函数的执行次数,前者是通过setTimeout进行实现,后者通过一定时间间隔进行实现;
防抖函数
防抖函数(雏形)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="button" type="button" value="按钮">
</body>
<script type="text/javascript">
button.addEventListener("click",handleClick,false)
let temp = null;
function handleClick() {
if (temp){
clearTimeout(temp)
}
temp = setTimeout(function () {
console.log("触发了")
},1000)
}
</script>
</html>
防抖函数1.0(使用了闭包)
利用闭包,此时temp的赋值能被记录下来且可以被handleCilek函数使用(访问),解决了雏形版本的temp变量声明函数里却每次执行都是null;声明在全局又不太合适。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="button" type="button" value="按钮">
</body>
<script type="text/javascript">
button.addEventListener("click",debounce(),false)
function debounce() {
let temp = null;
return function handleClick() {
if (temp){
clearTimeout(temp)
}
temp = setTimeout(function () {
console.log("触发了")
},1000)
}
}
</script>
</html>
防抖函数2.0(此版本开始才称得上是封装)
此时希望封装程度达到只要把处理函数扔给防抖函数就不用关心别的,直接就能够拥有防抖的功能;
其实就是我按正常情况我们来声明自己的handleClick(这里我称之为处理函数),然后把他传递给debounce函数,由debounce函数返回回调函数供事件监听回调,回调函数里头会执行我们定义好的处理函数;同时我们希望控制执行频率/时间间隔(timer)的阈值由调用者来决定。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="button" type="button" value="按钮">
</body>
<script type="text/javascript">
button.addEventListener("click",debounce(handleClick,2000),false)
function handleClick() {
console.log("触发了")
}
function debounce(fn,timer) {
let temp = null;
return function callback() {
if (temp){
clearTimeout(temp)
}
temp = setTimeout(function () {
fn()
},timer)
}
}
</script>
</html>
防抖函数3.0(希望第一次点击就立即触发)
此版本效果是:当第一次点击时候会立即触发,如果以小于timer的频率(时间间隔)继续持续点击是没有反应的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="button" type="button" value="按钮">
</body>
<script type="text/javascript">
button.addEventListener("click",debounce(handleClick,1000),false)
function handleClick() {
console.log("触发了")
}
function debounce(fn,timer) {
let temp = null;
return function callback() {
if (temp){
clearTimeout(temp)
}
let firstClick = !temp
if (firstClick){
fn()
}
temp = setTimeout(function () {
temp= null
},timer)
}
}
</script>
</html>
防抖函数4.0(解决this指向问题,传参的问题)
3.0,handleClick函数的this指向的是window全局对象。并且我们需要传参给handleClick在某些时候是必要的;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="button" type="button" value="按钮">
</body>
<script type="text/javascript">
button.addEventListener("click",(debounce(handleClick,2000)).bind(button,"传参"),false)
function handleClick() {
console.log(this)
console.log(arguments)
console.log("触发了",arguments[0])
}
function debounce(fn,timer) {
let temp = null;
return function callback() {
if (temp){
clearTimeout(temp)
}
let firstClick = !temp
if (firstClick){
fn.apply(this,arguments)
}
temp = setTimeout(function () {
temp= null
},timer)
}
}
</script>
</html>
防抖函数5.0(类似于技能释放需要冷却时间的模式)
前面4.0版本的防抖函数,虽然第一次可以触发,但是只要在timer时间间隔内持续的触发他就不会再进行触发了,而是需要停止timer时长再去触发才会再次触发。于是乎我们把4.0思想转变一下,判断是否要立即触发的标准是"技能“是否已经冷却好了,如果冷却好了 我就立即触发,没有冷却好我就直接return,直到冷却为止才允许触发。这样就能实现一旦持续触发我就在timer时间间隔内只触发一次。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="button" type="button" value="按钮">
</body>
<script type="text/javascript">
button.addEventListener("click",(debounce(handleClick,1000)).bind(button,"传参"),false)
function handleClick() {
console.log(this)
console.log(arguments)
console.log("触发了",arguments[0])
}
function debounce(fn,timer) {
let temp = null;
let coolDowned = true;
return function callback() {
if (!coolDowned){
return false
}
if (temp){
clearTimeout(temp)
}
if (coolDowned){
fn.apply(this,arguments)
}
coolDowned = false;
temp = setTimeout(function () {
coolDowned = true;
},timer)
}
}
</script>
</html>
防抖函数6.0(改变传参的方式)👈推荐
function debounce(fn,delay,...args) {
let temp = null;
let coolDowned = true;
return function callback() {
if (!coolDowned){
return false
}
if (temp){
clearTimeout(temp)
}
[].unshift.apply(arguments,args)
if (coolDowned){
fn.apply(this,arguments)
}
coolDowned = false;
temp = setTimeout(function () {
coolDowned = true;
},delay)
}
}
节流函数
节流函数1.0
节流函数通过时间戳的方式进行实现,第一次执行因为preTime = 0;currenTime是根据距离1970年1月1日毫秒数,第一次执行的时候 currentTime - currentTime 大于timer 执行fn,并且把当前事件赋值给preTime,下回触发就会根据距离上一次执行的时间戳进行判断是否大于timer,大于则执行。这个模式相当于上面写的防抖函数5.0的版本,显然这个实现会比上面那个写法更简单。大家可以根据需求进行选择;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input id="button" type="button" value="按钮">
</body>
<script type="text/javascript">
button.addEventListener("click",(throttle(handleClick,2000)).bind(button,"传参"),false)
function handleClick() {
console.log(this)
console.log(arguments)
console.log("触发了",arguments[0])
}
function throttle(fn,timer) {
let preTime = 0;
return function callback() {
let currentTime = new Date().getTime()
if (currentTime-preTime > timer){
fn.apply(this,arguments)
preTime = currentTime
}
}
}
</script>
</html>
节流函数2.0(改变传参的方式)👈推荐
function throttle(fn,delay,...args) {
let preTime = 0;
return function callback() {
[].unshift.apply(arguments,args)
let currentTime = new Date().getTime()
if (currentTime-preTime > delay){
fn.apply(this,arguments)
preTime = currentTime
}
}
}
最终(防抖函数6.0-节流函数2.0)使用方式
我已经将其发布到npm供大家下载使用了
npm install debounce-throttle-args --save
let {debounce,throttle} = require('debounce-throttle-args')
document.querySelector(".debounce-button").addEventListener("click",debounce(handleClick,2000,"zhangsan","lisi"))
document.querySelector(".throttle-button").addEventListener("click",throttle(handleClick,2000,"wangwu","zhaoliu"))
function handleClick(name1,name2) {
console.log(this)
console.log(event)
console.log(arguments)
console.log(name1,name2)
}