云破日出,你是那道光束

0%

防抖和节流

防抖

原由:

为了防止用户频繁触发某一事件,导致的卡顿,堵塞,崩溃的现象,优化性能和体验

应用场景:

  1. scroll事件滚动触发
  2. 框输入查询
  3. 表单验证
  4. 按钮提交事件
  5. 浏览器窗口缩放,resize事件

防抖原理:

事件响应函数(dosomeThing)在一段时间后(时长自己定义:如300ms)才执行,如果在这段时间内再次调用,则重新计算执行时间;当预定的时间内没有再次调用该函数,则执行该函数(dosomeThing)。

第一种方法:引入js库underscore.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
引入别人封装好的js文件(underscore.js)CDN引入:
https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js
<div class="container" style="width: 100%;height: 300px;background-color: skyblue;text-align: center;font-size: 50px;"></div>
<script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js"></script>
<script >
let container = document.querySelector(".container");
let count = 0 ;
function dosomeThing(e){
<!-- event指向问题 -->
console.log(e);
<!-- 改变执行函数内部this的指向 -->
console.log(this);
<!-- 可能会做回调或者ajax请求 -->
container.innerHTML = count++;
return "结果"
}
// 高阶函数 防抖 调用防抖函数 _.debounce()
container.onmousemove = _.debounce(dosomeThing,300);
//参数,第一:处理事件函数,第二:触发时间,第三:默认false(true为立即执行函数)
// _.debounce(dosomeThing,300);
// container.onmousemove = dosomeThing; // 不做处理
</script>

第二种方法:手写简版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script type="text/javascript">
function fengZn(fn,wait){
var timer;
return function(){
<!-- 1.清除定时器; -->
clearTimeout(timer);
<!-- 2.重新注册定时器; -->
timer = setTimeout(()=>{
console.log(fn);
fn()
},wait)
}
}
function resizeEvent(){
console.log("他扒拉我")
}
window.addEventListener("resize",fengZn(resizeEvent,2000));
</script>

第三种方法:模拟库,手写封装一个健壮版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<div class="container" style="width: 100%;height: 300px;background-color: skyblue;text-align: center;font-size: 50px;"></div>
<button class="btn">取消防抖</button>
<script>
let container = document.querySelector(".container");
let count = 0 ;
// 触发后,处理事件。
function dosomeThing(){
container.innerHTML = count++;
}
<!-- 传入参数处理事件函数,触发时间,是否立即执行 -->
function debounce(func,wait,immediate){
var timeout,result;
let decounced = function(){
<!-- 重置this,args指向 -->
var context = this;
var args = arguments;
if(timeout) clearTimeout(timeout);
if(immediate){
var callNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
},wait);
<!-- 立即执行 -->
if(callNow) result = func.apply(context,args);
}else{
<!-- 不会立即执行 -->
timeout = setTimeout(function(){
func.apply(context,args);
},wait);
}
return result;
}
<!-- 取消防抖,定义一个取消方法cancel -->
decounced.cancel = function(){
clearTimeout(timeout)
<!-- 防止内存泄露 -->
timeout = null;
}
return decounced;
}
let btn = document.querySelector(".btn");
let dosome = debounce(dosomeThing,1000);
container.onmousemove = dosome;
// 取消防抖
btn.onclick = function(){
dosome.cancel()
}
</script>

节流

原由:

防止多次调用函数,减少不必要的性能损耗

应用场景:

  1. DOM元素的拖拽功能实现
  2. 射击游戏
  3. 计算鼠标移动的距离
  4. 监听scroll滚动事件
  5. 点击事件

节流原理:

如果你持续触发事件,每隔一段时间,只执行一次事件

第一种方法:引入js库和 防抖 一样 CDN 引入 underscore.js文件,在调用函数方法时:

注意: 是调用方法: _.throttle()
_.throttle() ,传参的第三参数(可不传)是个对象,有两个变量可设置

leading: false, 设置是否禁止第一次触发执行
trailing: false 设置是否禁止最后一次触发执行

1
2
// 高阶函数 防抖 调用节流函数方法 _.throttle()
container.onmousemove = _.throttle(dosomeThing,300);

第二种方法:利用定时器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script type="text/javascript">
function throttle(fn){
var canRun = true;
return function(){
<!-- 如果开关关闭,则不允许执行后续语句 -->
if(!canRun) return;
fn()
canRun = false;
<!-- 在一定延迟之后,将开关变成true; -->
setTimeout(()=>{
canRun = true;
},1000)
}
}
<!-- resize 回调事件/事件处理程序 -->
function conName(){
console.log("他扒拉我")
}
window.addEventListener("resize",throttle(conName));
<!-- scroll 回调事件/事件处理程序 -->
function scrollEvent(){
<!-- 滚动时要做的事情 -->
console.log("滚动")
}
window.addEventListener("scroll",throttle(scrollEvent));
</script>

第三种方法:模拟 underscore.js

触发后调用相关函数方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14

//利用underscore.js库
// let dosome = _throttle(dosomeThing,1000,{
// leading: false,
// trailing: true //禁止最一次触发执行
// });

// let dosome = throttle(dosomeThing,2000);

let dosome = throttle(dosomeThing,1000,{
leading: false,
trailing: true //禁止最一次触发执行
});
container.onmousemove = dosome;

1. 利用时间戳

第一次会触发,最后一次会触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function throttle(func,wait){
let context,args;
// 之前的时间戳
let oldTime = 0;
return function(){
context = this;
args = arguments;
// 获取当前的时间戳
let nowTime = new Date().valueOf();
if(nowTime-oldTime > wait){
// 立即执行
func.apply(context,args);
oldTime = nowTime;
}
}
}

2. 利用定时器

第一次不会触发,最后一次会触发

1
2
3
4
5
6
7
8
9
10
11
12
13
function throttle(func,wait){
let context,args,timeout;
return function(){
context = this;
args = arguments;
if(!timeout){
timeout = setTimeout(()=>{
func.apply(context,args);
timeout = null;
},wait)
}
}
}

3. 结合时间戳和定时器使用

第一次直接执行,最后一次还会执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function throttle(func,wait){
let context,args,timeout;
// 之前的时间戳
let oldTime = 0;
let later = function(){
oldTime = new Date().valueOf();
timeout = null;
func.apply(context,args);
}
return function(){
context = this;
args = arguments;
// 获取当前的时间戳
let nowTime = new Date().valueOf();
if(nowTime-oldTime > wait){
if(timeout){
clearTimeout(timeout);
timeout = null;
}
// 立即执行
func.apply(context,args);
oldTime = nowTime;
}else if(!timeout){
timeout = setTimeout(later,wait)
}
}
}

4. 节流优化

按需求控制第一次和最后一次触发的是否执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

function throttle(func,wait,options){
let context,args,timeout;
// 之前的时间戳
let oldTime = 0;
if (!options) options = {};
let later = function(){
oldTime = new Date().valueOf();
timeout = null;
func.apply(context,args);
}
return function(){
context = this;
args = arguments;
// 获取当前的时间戳
let nowTime = new Date().valueOf();
if(options.leading === false && !oldTime){
oldTime = nowTime
}
if(nowTime - oldTime > wait){
// 第一次会直接执行
if(timeout){
clearTimeout(timeout);
timeout = null;
}
func.apply(context,args);
oldTime = nowTime;
}else if(!timeout && options.trailing !== false){
// 最后一次也会被执行
timeout = setTimeout(later,wait)
}
}
}
-------- 本文结束 感谢阅读 --------
喜欢的请打赏喔!!!.