温顾篇 —— JS(函数防抖debounce)
温顾篇 —— JS(函数防抖debounce)
在前端中有一些原生事件会频繁的触发从而容易造成页面的卡顿,例如:
- window 的 resize、scroll(页面滚动加载等)
- mousedown、mousemove
- keyup、keydown
- input 的 input 事件(调用接口查询等)
接下来我就用原生来简单写下js函数的防抖:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" class="ipt">
<script>
function debounce(fn, delay) {
let timer = null;
return function (e) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
// 这里最好不要直接调用fn fn(), 因为this指向window 而且event事件是undefined
fn()
// fn.apply(this, arguments)
}, delay)
}
}
// 获取节点
let ipt = document.querySelector('.ipt')
// 绑定事件触发防抖函数
ipt.addEventListener('input', debounce(function (event) {
console.log('this', this);
console.log('event', event);
},5000))
</script>
</body>
</html>
注意:如果防抖函数中直接调用fn函数,则会出现如下this的指向问题和绑定事件的event为undefined;
还有这里传入的函数,也不要是箭头函数,也会导致this指向window;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" class="ipt">
<script>
function debounce(fn, delay) {
let timer = null;
return function (e) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
// 这里最好不要直接调用fn fn(), 因为this指向window 而且event事件是undefined
// fn()
fn.apply(this, arguments)
}, delay)
}
}
// 获取节点
let ipt = document.querySelector('.ipt')
// 绑定事件触发防抖函数
ipt.addEventListener('input', debounce(function (event) {
console.log('this', this);
console.log('event', event);
},5000))
</script>
</body>
</html>
注意:调用时 使用apply改变函数的this指向,fn.apply(this, arguments)
解析如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" class="ipt">
<script>
function debounce(fn, delay) {
return function (e) {
console.log('debounce 函数执行了');
}
}
// 获取节点
let ipt = document.querySelector('.ipt')
// 绑定事件触发防抖函数 addEventListener调用的是函数,所以debounce需要返回一个函数
// 这里只会返回debounce函数中的逻辑, 并不会打印 console.log('发送请求');
ipt.addEventListener('input', debounce((event) => {
console.log('发送请求');
}, 5000))
</script>
</body>
</html>
1、如果想触发打印 console.log('发送请求');
怎么办呢?
在return返回的函数中执行fn函数既可;
2、如果不想立即执行fn函数,怎么办呢?
这时候想到可以用个定时器来控制,这时候就可以控制什么时候执行fn函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" class="ipt">
<script>
function debounce(fn, delay) {
return function (e) {
console.log('debounce 函数执行了');
setTimeout(() => {
fn()
}, delay)
}
}
// 获取节点
let ipt = document.querySelector('.ipt')
// 绑定事件触发防抖函数 addEventListener调用的是函数,所以debounce需要返回一个函数
ipt.addEventListener('input', debounce((event) => {
console.log('发送请求');
}), 5000)
</script>
</body>
</html>
3、如果不想调用fn函数多次呢?
判断定时器是否存在,存在则清掉之前的定时器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text" class="ipt">
<script>
function debounce(fn, delay) {
let timer = null;
return function (e) {
console.log('debounce 函数执行了');
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn()
}, delay)
}
}
// 获取节点
let ipt = document.querySelector('.ipt')
// 绑定事件触发防抖函数 addEventListener调用的是函数,所以debounce需要返回一个函数
ipt.addEventListener('input', debounce((event) => {
console.log('发送请求');
}, 5000))
</script>
</body>
</html>
防抖函数如下:
function debounce(fn, delay) {
let timer = null;
return function (e) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
// 这里最好不要直接调用fn fn(), 因为this指向window 而且event事件是undefined
fn.apply(this, arguments)
}, delay)
}
}
// 传参不同
function debounce(fn, delay) {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
// 这里最好不要直接调用fn fn(), 因为this指向window 而且event事件是undefined
fn.apply(this, args)
}, delay)
}
}