js性能优化之防抖与节流
防抖
函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
节流
函数节流(throttle):当持续触发事件时,保证一定时间段内只调用一次时间处理函数。
节流的定义应该比较好理解,举例来说就是,你在疯狂点击短视频里面的“点亮红心”按钮,比如你10s点击了1000次,但是节流的做法就是在这1000次的事件触发过程中,我规定1s才能真正的触发红心亮起的逻辑。所以10s内再快的点击频率,也只会亮起10颗红心。
防抖的定义看起来比较绕,我们也举例说明。举例某宝或者某东的联想搜索框,正常情况,如果你短时间内不停的敲击键盘那么每一次输入的内容都会发送请求给服务端做联想算法,然后不停返回数据,那么下拉框看起来就会不停闪动,体验很差,并且实际上不需要那么高频率的去发送请求,应该是当你停止了一个字或者一个词或者一句话的输入后再发送请求。所以防抖的做法,就是无论你多快的输入(触发事件),我只在这个周期的开始或者结束触发一次事件中的逻辑。如此反复,视为防抖。
防抖的应用场景
- search搜索联想,用户不短输入,用防抖节约请求资源,优化用户体验
- window触发resize的时候,不断调整浏览器窗口大小会触发这个事件,利用防抖来让其触发一次
- 防止短时间内重复提交
节流的应用场景
- 鼠标不短触发mousedown或者mouseover事件的时候,可以利用节流在单位事件内只触发一次
- 监听滚动事件,比如是否滑到底部加载更多数据,用节流来判断
防抖的实现
我们设定一个场景,有一个拥有固定宽高的div,监听这个div的mouseover事件,触发一次就+1,并将值填充到div中。
防抖的目的就是为了让鼠标移动的时候,按照规定的周期结束或者开始的时候才触发这个+1的逻辑。
<html>
<head>
<title></title>
<style type="text/css">
#container{
width: 400px;
height: 400px;
background-color: #333333;
color:#ffffff;
display: flex;
justify-content: center;
align-items: center;
font-size: 34px;
font-weight: 600;
}
</style>
</head>
<body>
<div id="container"></div>
<script type="text/javascript">
var count = 1;
var container = document.getElementById('container');
function getUserAction(){
this.innerHTML = count++;
}
container.onmousemove = debounce(getUserAction,100,false);
function debounce(fn,wait,flag){
var timeout;
return function(){
var self = this;
clearTimeout(timeout);
if(flag){
var callNow = !timeout;
timeout = setTimeout(function(){
timeout = null;
}, wait);
if(callNow){
fn.apply(self);
}
}else{
timeout = setTimeout(function(){
fn.apply(self);
}, wait);
}
}
}
</script>
</body>
</html>
复制代码
节流的实现
继续引用上述场景,节流的目的是为了在鼠标移动不停触发mouseover的时候,+1的逻辑只在规定的周内执行一次(例:鼠标疯狂滑动,只在1s内+1,不会在1s中内触发两次函数)
<html>
<head>
<title></title>
<style type="text/css">
#container{
width: 400px;
height: 400px;
background-color: #333333;
color:#ffffff;
display: flex;
justify-content: center;
align-items: center;
font-size: 34px;
font-weight: 600;
}
</style>
</head>
<body>
<div id="container"></div>
<script type="text/javascript">
var count = 1;
var container = document.getElementById('container');
function getUserAction(){
this.innerHTML = count++;
}
container.onmousemove = throttle(getUserAction,1000);
//代理模式
function throttle(fn,wait){
var pre = 0;
return function(){
var self = this;
var now = +new Date();
if(now-pre>wait){
fn.apply(self);
pre = now;
}
}
}
</script>
</body>
</html>