首先,我们需要先搭建一个页面用来测试
<!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>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="Box"></div>
<div id="BoxTwo">
<ul>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
<li>1</li>
</ul>
</div>
<script src="./js.js"></script>
</body>
</html>
样式:
body{
margin: 0;
}
#Box{
width: 600px;
height: 600px;
background: #f1f1f1;
border: 1px solid #000;
margin: 100px auto 0;
}
#BoxTwo{
width: 600px;
height: 600px;
background: #f1f1f1;
border: 1px solid #000;
margin: 100px auto 20px;
overflow-y: scroll;
}
防抖:
为什么使用防抖?
原因:
如果我们监听一个页面的滚动或者监听一个输入框用户的输入,那么每一次滚动或者每一次输入都会触发对应的监听方法
如果方法中进行了比较复杂的操作,在这一次触发的计算还没计算完成之前,又触发了下一次,就会造成浏览器的卡顿
这时我们就要对这些监听进行一个优化 避免高频率的触发
应用场景:
高频率触发一个函数
注意 : 高频率回调函数也会造成卡顿的情况 所以我们可以设置一个停顿的时间
原理:
当监听事件触发后一段事件没有再次触发,则执行事件,如果在执行之前再次触发,则重新计时
代码:
function debounce(func, wait) { // 防抖函数1 初级
let timeOut; // 计时器
// console.log(this); 这里的this指向window
// 这里使用了闭包的写法,所以这个timeOut是属于下面这个函数的全局变量
return function () {
// console.log(this); 这里的this指向Box元素
clearTimeout(timeOut); // 销毁计时器
timeOut = setTimeout(() => {
func(1); // 传递参数
}, wait) // 将计时器赋值给timeOut
}
}
function debounceTwo(func, wait) { // 防抖函数2 通过apply优化了this的指向 通过arguments优化了参数传递的问题
let timeOut;
return function () {
let that = this
let args = arguments
clearTimeout(timeOut)
timeOut = setTimeout(function () {
func.apply(that, args)
}, wait)
}
}
function debounceThree(func, wait, immediate) { // 防抖函数3 加入了首次触发的功能 通过第三个参数控制
let timeOut; // 计时器
return function () {
if (timeOut) clearTimeout(timeOut); // 有计时器才能销毁计时器
var that = this;
var args = arguments;
if (immediate) {
func.apply(that, args)
immediate = false
} else {
timeOut = setTimeout(function () {
func.apply(that, args)
}, wait)
}
}
}
function boxMouseFun() {
console.log(this);
console.log(arguments);
}
(function () {
let Box = document.querySelector("#Box");
Box.addEventListener('mousemove',boxMouseFun) // 鼠标一直移动的话 这个事件就会被持续性不间断的触发 会给浏览器带来一些负担
Box.addEventListener('mousemove', debounce(boxMouseFun, 2000));
// 这样已经实现了简单的防抖
// 这里有个问题 我们的boxMouseFun方法是由Box元素调用起来的 正常来说 这个方法的this应该指向Box元素 而现在指向的是window
Box.addEventListener('mousemove',debounceTwo(boxMouseFun,2000));
// 我们需要一个首次触发一次 然后再次执行时触发防抖函数的防抖功能
Box.addEventListener('mousemove', debounceThree(boxMouseFun, 2000, true)); // 优化了首次执行后再次触发是防抖函数
})()
节流:
为什么使用节流?
原因:
当我们持续触发某一个事件时 例如我们下拉刷新来请求一个接口 如果闲着没事频繁的下滑 频繁的请求接口 十分的消耗性能 因为请求一般来说都是异步请求 在这一次的数据返回之前 下一次请求又来了 会十分的卡顿
应用场景:
图片懒加载
数据请求
原理:
当持续触发事件时,保证一定时间段内只调用一次事件处理函数
代码:
function throttle(func, time) { // 时间戳原理节流
let startDate = Date.now(); // 开始时间的时间戳
return function () {
let that = this
let args = arguments
let triggerDate = Date.now(); // 结束时间的时间戳
if (triggerDate - startDate >= time) {
func.apply(that, args);
startDate = Date.now()
}
}
}
function throttleTwo(func, time) { // 定时器原理节流
let timer = null; // 定时器
return function () {
if (!timer) { // 定时器为空时可触发
let that = this
let args = arguments
timer = setTimeout(function () { // 给定时器赋值
func.apply(that, args)
timer = null // 重置定时器
}, time)
}
}
}
function throttleThree(func,time){ // 时间戳优化版
let startTime=0,that,args;
return function(){
let triggerTime = Date.now(); // 触发时间
if(triggerTime - startTime > time){
func.apply(that,args);
startTime = triggerTime
}
}
}
function BoxScrollFunc() {
// console.log(this); // 指向元素
console.log(1111);
}
(function () {
let Box = document.querySelector("#BoxTwo");
Box.addEventListener('scroll', throttle(BoxScrollFunc, 1000)) // 触发时间的时间戳和触发后更新开始时间的时间戳来实现节流 首次立即执行 然后一秒内不可再次触发
Box.addEventListener('scroll', throttleTwo(BoxScrollFunc,1000)) // 定时器初始化为null 触发后给定时器赋值 有值的定时器不在触发事件 触发事件后再给定时器赋值为null 如此反复实现节流 首次一秒后执行 然后一秒内不可再次触发
Box.addEventListener('scroll',throttleThree(BoxScrollFunc,1000)) // 时间戳节流的优化版本 初始时间设为0 通过触发事件减去初始时间大于间隔时间触发事件 触发事件后将初始时间赋值为触发时间
})()
节流和防抖的总结:
防抖: 在频繁的触发事件后一段时间执行事件
节流: 在频繁的触发事件中控制,一段时间最多只能触发一次事件