干嘛的?限制函数调用的频率。
为什么要限制?例如搜索的时候请求联想建议,没必要每次内容改变就发请求,当用户输入的很快的时候,中间的请求都是无意义的,浪费资源,没必要。
函数防抖(Debounce)
概念: 在事件被触发n秒后再执行,如果在这n秒内又被触发,则重新计时。
生活中的实例: 如果有人进电梯(触发事件),那电梯将在10秒钟后出发(执行事件监听器),这时如果又有人进电梯了(在10秒内再次触发该事件),我们又得等10秒再出发(重新计时)。
我们先使用第三方包 lodash 来体验什么是函数防抖:
首先把 lodash 安装到项目中:
# yarn add lodash
npm i lodash
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<script src="./node_modules/lodash/lodash.js"></script>
<script>
// lodash 会在全局提供一个成员:_
// _ 对象中有很多方法,其中有一个方法专门用于处理函数防抖
// 方法名:debounce
// 作用:函数防抖
// 使用方式:
function fn(foo) {
console.log("hello", foo);
}
// 正常的函数调用:立即调用,而且是一定会调用
// fn()
// fn()
// fn()
// 我们可以使用函数防抖把一个正常的函数变得不正常
// 两个参数:
// 参数1:函数
// 参数2:时间,单位是毫秒
// 返回值:函数
// 返回值函数的功能和 fn 和的功能是一样
// 唯一的区别就是经过了防抖处理
const newFn = _.debounce(fn, 1000);
// 计时 1s
newFn("a");
// 当你不到 1s 的时候,再次调用
// 先把之前的废掉,重新计时 1s
newFn("b");
newFn("b");
newFn("b");
// newFn()
// he
</script>
</body>
</html>
函数防抖实现原理
函数防抖的实现原理:
function fn(foo) {
console.log("hello", foo);
}
const newFn = debounce(fn, 1000);
// 计时 1s
newFn(123);
// 如果在 1s 之内重新调用
// 先把之前的废除
// 重新计时
newFn("world");
// newFn()
function debounce(callback, time) {
let timer = null;
// 函数参数中的 ... 表示接收剩余参数
// 它会把所有的参数收集到一个数组中
return function(...args) {
console.log(args);
window.clearTimeout(timer);
timer = setTimeout(() => {
// 这里的 ... 表示数组展示操作符
// args[0], args[1], args[2] .........
callback(...args);
}, time);
};
}
函数节流(Throttle)
概念: 规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
生活中的例子:函数节流就是开枪游戏的射速,就算一直按着鼠标射击,也只会在规定射速内射出子弹。
我们先用 lodash 来体验节流的使用方式:
function fn() {
console.log("------ fire ------");
}
// 参数1:函数
// 参数2:间隔时间
// 返回值:函数(它的功能和保证的 fn 的功能是一样的,但是被进行了节流处理)
// 第1次直接调用,之后的按照一定频率进行调用
const newFn = _.throttle(fn, 2000);
// newFn()
// newFn()
setInterval(() => {
console.log("鼠标点击");
newFn();
}, 200);
// 一上来就调用一次
// newFn()
// // 之后的调用,开始计时 1s
// newFn()
// // 1s 之内所有的调用只有1次
// newFn()
// newFn()
// newFn()
// newFn()
// newFn()
函数节流实现原理
function throttle(callback, interval) {
// 最后一次的调用时间
let lastTime = 0;
// 定时器
let timer = null;
// 返回一个函数
return function() {
// 清除定时器
clearTimeout(timer);
// 当前最新时间
let nowTime = Date.now();
// 如果当前最新时间 - 上一次时间 >= 时间间隔
// 或者没有上一次时间,那就立即调用
if (nowTime - lastTime >= interval) {
callback();
// 记录最后一次的调用时间
// 1
lastTime = nowTime;
} else {
timer = setTimeout(() => {
callback();
}, interval);
}
};
}
const fn = throttle(函数, 1000);
//
fn();
fn();
fn();
函数节流或者防抖的相关场景
- 建议
window.resize事件处理- 页面的滚动事件
总结
- 函数防抖和函数节流都是防止某一时间频繁触发,但是这两兄弟之间的特性却不一样。
search搜索联想,用户在不断输入值时,用防抖来节约请求资源。window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
- 函数防抖是某一段时间内只执行一次,而函数节流是间隔时间执行。
- 鼠标不断点击触发,
mousedown(单位时间内只触发一次) - 监听滚动事件,比如是否滑到底部自动加载更多,用
throttle来判断
- 鼠标不断点击触发,
lodash函数库
lodash 是一个常用工具函数库,它里面封装了好多常用的工具函数:
在
lodash之前还有一个 JavaScript 工具函数库:underscore,lodash模仿underscope 自己重新实现了一遍,它相比 underscore 最大的好处是它支持按需加载。underscope不支持按需加载。