防抖
我们可以通过一幅图来认识这个过程
- 当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间;
- 当事件密集触发时,函数的触发会被频繁的推迟;
- 只有等待了一段时间也没有事件触发,才会真正的执行响应函数;
应用场景
- 搜索框输入数据,触发事件
- 频繁的点击按钮
- 监听屏幕滚动事件
- 用户窗口的resize事件
防抖的意义:只有在某个时间内,没有再次触发某个函数时,才真正的调用这个函数;
undercore实现
<!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>
<button>按钮</button>
<input type="text">
<!-- CDN引入: 网络的js文件 -->
<script src="https://cdn.jsdelivr.net/npm/underscore@1.13.4/underscore-umd-min.js"></script>
<!-- 本地引入: 下载js文件, 并且本地引入 -->
<!-- <script src="./js/underscore.js"></script> -->
<script>
// 1.获取input元素
const inputEl = document.querySelector("input")
// 2.监听input元素的输入
// let counter = 1
// inputEl.oninput = function() {
// console.log(`发送网络请求${counter++}:`, this.value)
// }
// 3.防抖处理代码
let counter = 1
inputEl.oninput = _.debounce(function() {
console.log(`发送网络请求${counter++}:`, this.value)
}, 3000)
</script>
</body>
</html>
防抖基本功能实现:可以实现防抖效果
优化一:优化参数和this指向
优化二:优化取消操作(增加取消功能)
优化三:优化立即执行效果(第一次立即执行)
优化四:优化返回值
基本实现:
const hyDebounce = function (fn,delay){
//1.第一次触发的timer
let timer = null
//2.触发执行后的函数
const _debounce = function(...args){
//如果再次触发,取消上一次的事件
if (timer) clearTimeout(timer)
//延迟执行对应的回调函数
timer = setTimeout(()=>{
fn()
//执行完函数后timer置为null
timer = null
},delay)
}
//返回函数(闭包)
return _debounce
}
this优化
function hydebounce(fn, delay) {
// 1.用于记录上一次事件触发的timer
let timer = null
// 2.触发事件时执行的函数
const _debounce = function(...args) {
// 2.1.如果有再次触发(更多次触发)事件, 那么取消上一次的事件
if (timer) clearTimeout(timer)
// 2.2.延迟去执行对应的fn函数(传入的回调函数)
timer = setTimeout(() => {
fn.apply(this, args)
timer = null // 执行过函数之后, 将timer重新置null
}, delay);
}
// 返回一个新的函数
return _debounce
}
取消功能
const hyDebounce = function (fn,delay){
//1.第一次触发的timer
let timer = null
//2.触发执行后的函数
const _debounce = function(...args){
//如果再次触发,取消上一次的事件
if (timer) clearTimeout(timer)
//延迟执行对应的回调函数
timer = setTimeout(()=>{
//绑定this
fn.apply(this,args)
//执行完函数后timer置为null
timer = null
},delay)
}
//取消函数
_debounce.cancel = function (){
if(timer) clearTimeout(timer)
timer = null
}
//返回函数(闭包)
return _debounce
}
立即执行功能
// 原则: 一个函数进行做一件事情, 一个变量也用于记录一种状态
const hyDebounce = function (fn,delay,immediate = true){
//1.第一次触发的timer
let timer = null
let isInvoke = false
//2.触发执行后的函数
const _debounce = function(...args){
//如果再次触发,取消上一次的事件
if (timer) clearTimeout(timer)
//第一次是否不需要延迟
if(immediate && !isInvoke){
fn.apply(this,args)
isInvoke = true
return
}
//延迟执行对应的回调函数
timer = setTimeout(()=>{
//绑定this
fn.apply(this,args)
//执行完函数后timer置为null
timer = null
isInvoke = false
},delay)
}
//取消函数
_debounce.cancel = function (){
if(timer) clearTimeout(timer)
timer = null
isInvoke = false
}
//返回函数(闭包)
return _debounce
获取返回值
const hyDebounce = function (fn,delay,immediate = true){
//1.第一次触发的timer
let timer = null
let isInvoke = false
//2.触发执行后的函数
const _debounce = function(...args){
return new Promise((resolve,reject)=>{
try {
//如果再次触发,取消上一次的事件
if (timer) clearTimeout(timer)
let res = undefined //返回值
//第一次是否不需要延迟
if(immediate && !isInvoke){
res = fn.apply(this,args)
resolve(res)
isInvoke = true
return
}
//延迟执行对应的回调函数
timer = setTimeout(()=>{
//绑定this
res = fn.apply(this,args)
resolve(res)
//执行完函数后timer置为null
timer = null
isInvoke = false
},delay)
}catch (err){
reject(err)
}
})
}
//取消函数
_debounce.cancel = function (){
if(timer) clearTimeout(timer)
timer = null
isInvoke = false
}
//返回函数(闭包)
return _debounce
}
封装独立文件
// 原则: 一个函数进行做一件事情, 一个变量也用于记录一种状态
function hydebounce(fn, delay, immediate = false, resultCallback) {
// 1.用于记录上一次事件触发的timer
let timer = null
let isInvoke = false
// 2.触发事件时执行的函数
const _debounce = function(...args) {
return new Promise((resolve, reject) => {
try {
// 2.1.如果有再次触发(更多次触发)事件, 那么取消上一次的事件
if (timer) clearTimeout(timer)
// 第一次操作是不需要延迟
let res = undefined
if (immediate && !isInvoke) {
res = fn.apply(this, args)
if (resultCallback) resultCallback(res)
resolve(res)
isInvoke = true
return
}
// 2.2.延迟去执行对应的fn函数(传入的回调函数)
timer = setTimeout(() => {
res = fn.apply(this, args)
if (resultCallback) resultCallback(res)
resolve(res)
timer = null // 执行过函数之后, 将timer重新置null
isInvoke = false
}, delay);
} catch (error) {
reject(error)
}
})
}
// 3.给_debounce绑定一个取消的函数
_debounce.cancel = function() {
if (timer) clearTimeout(timer)
timer = null
isInvoke = false
}
// 返回一个新的函数
return _debounce
}
节流
我们可以通过一幅图来认识这个过程
节流的应用场景:
➢ 监听页面的滚动事件;
➢ 鼠标移动事件;
➢ 用户频繁点击按钮操作;
➢ 游戏中的一些设计
我们按照如下思路来实现:
节流函数的基本实现:可以实现节流效果
优化一:节流最后一次也可以执行
优化二:优化添加取消功能
优化三:优化返回值问题
基本实现
function myThrottle(fn,interval){
let startTime = 0
return _throttle = function (){
let nowTime = new Date().getTime()
let waitTime = interval - (nowTime - startTime)
if(waitTime <= 0){
fn()
startTime = nowTime
}
}
}
this和参数绑定
function myThrottle(fn,interval){
let startTime = 0
return _throttle = function (...args){
let nowTime = new Date().getTime()
let waitTime = interval - (nowTime - startTime)
if(waitTime <= 0){
fn.apply(this,args)
startTime = nowTime
}
}
}
对立即执行进行控制
引入一个变量 leading 对立即执行进行控制
function myThrottle(fn, interval, leading = true) {
let startTime = 0
return _throttle = function(...args) {
let nowTime = new Date().getTime()
// 控制是否立即执行
if (!leading && startTime === 0) {
startTime = nowTime
}
let waitTime = interval - ( nowTime - startTime)
if (waitTime <= 0) {
fn.apply(this, args)
startTime = nowTime
}
}
}
现有的封装好的
undercore
lodash