前言:要想真正理解节流和防抖,首先了解:this指向、闭包、apply()、计时器等知识。
概念:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
当一个事件触发频率很高时,在事件触发的n秒内又触发事件,比如连续点击按钮,就会将等待时间重置。在最后一次事件触发后的 n 秒才执行函数。
比如一个人进入电梯,电梯会等待5s,当不断有人进电梯时,电梯都会重置等待事件,当最后一个人进入电梯的5s后电梯才会关闭。
还比如公交车等乘客,要在最后一个乘客上车后才关门。
官方语法: _.debounce(func, wait, [immediate])
// 最简单的防抖
function debounce(fn,wait){
var timer = null;
return function(){ // 闭包
// debounce只会执行一次,后面执行的都是返回的这个函数
if(timer !== null){
clearTimeout(timer);
}
timer = setTimeout(fn,wait);
}
}
function handle(){
console.log(Math.random());
}
window.addEventListener("resize",debounce(handle,1000));
// 解决this指向,this应该指向的是当前触发事件的对象
// 解决参数问题,e 指向的是触发事件的对象
// 解决立即执行问题
<body>
<button id="btn">点击</button>
</body>
<script>
function debounce(fun,wait,immediate){
let timeo;
return function(){
let context = this;
let args = arguments;
clearTimeout(timeo);
if(immediate){ // 如果为立即执行为true 进入if
let callnow = !timeo; //
timeo = setTimeout(function () {
timeo = null;
fun.apply(context,args);
},wait)
if(callnow) fun.apply(context,args);
}else{
timeo = setTimeout(function(){
fun.apply(context,args);
},wait);
}
}
}
function handle(){
console.log(Math.random());
}
// 是否立即执行
let immediate = true;
let btn = document.getElementById('btn');
btn.addEventListener('click',debounce(handle,1000,immediate))
// 加上可以取消防抖和拥有返回值
<body>
<button id="btn" class="btn">触发</button>
<button id="btncl" class="btn">取消</button>
</body>
<script>
function debounce(fun,wait,immediate){
let timeo,re;
let debounce = function(){
let context = this;
let args = arguments;
clearTimeout(timeo);
if(immediate){
let callnow = !timeo;
timeo = setTimeout(function () {
fun.apply(context,args);
timeo = null;
},wait)
if(callnow) re = fun.apply(context,args);
}else{
timeo = setTimeout(function(){
fun.apply(context,args);
},wait);
}
return re;
}
debounce.cancel = function (){
clearTimeout(timeo);
timeo = null; // 因为是闭包 所以要置空 解决内存泄漏
}
return debounce;
}
function handle(){
console.log(Math.random());
}
let immediate = false;
let btn = document.getElementById('btn');
let btncl = document.getElementById('btncl');
let result = debounce(handle,3000,immediate);
btn.addEventListener('click',result);
btncl.addEventListener('click',result.cancel);
</script>
应用实例:
搜索框输入查询
按钮提交事件
浏览器窗口的缩放
scroll事件滚动触发
概念:指连续触发事件每隔 n 秒执行一次函数。
就像喝水,你一直喝一直喝,但是会每间隔一段时间去上厕所,而不会一直上厕所。
官方语法:_.throttle(function, wait, [options])
options中包含 leading ->开始是否执行 trailing ->结束是否执行
他们只能设置成 true true || false false || true false || false true
// 使用时间戳 大于时间周期就执行 第一次触发 最后不会触发
function throttle(fun,wait){
let context,args;
let old = 0;
return function(){
context = this;
args = arguments;
let now = new Date().valueOf();
if(now-old > wait){ // 第一次 now-0 是大于wait的 因为now非常的大 可以自行输出一下
fun.apply(context,args); // 马上执行
old = now; // 将时间戳记为当下
}
}
}
// 使用计时器 大于时间周期就执行 第一次不触发 最后会触发 不触头 触尾
function throttle(fun,wait){
let context,args,timeo;
return function(){
context = this;
args = arguments;
if(!timeo){ // 第一次!timeo 为true
timeo = setTimeout(() => { // timeo得到值,所以在wait时间内都不会进入这个if
fun.apply(context,args);
timeo = null; // 直到wait结束,将!timeo 又变成true
},wait)
}
}
}
<body>
<button id="btn" class="btn">触发</button>
<div></div>
<button id="btncl" class="btn">取消</button>
</body>
<script>
function throttle(fun,wait,options){
let context,args,timeo,re;
let old = 0;
if(!options) options = {};
let later = function(){
old = new Date().valueOf(); // 这里的作用就是不让函数进入时间戳的if代码中,而是继续计时wait执行
re = fun.apply(context,args);
timeo = null;
}
return function(){
context = this;
args = arguments;
let now = new Date().valueOf();
if(options.leading === false && !old){
// 第一次不执行
old = now;
}
if(now-old > wait){ // 第二次点击在wait范围内就不进入
// 第一次执行必进入
if(timeo){ // 这里的作用是防止还没到计时器的时间就又调用fun
clearTimeout(timeo);
timeo = null;
}
re = fun.apply(context,args);
old = now;
}else if(!timeo && options.trailing !== false){
// 最后一次执行必进入
timeo = setTimeout(later,wait)
}
return re;
}
}
function dothing(){
console.log(Math.random());
}
let btn = document.getElementById('btn');
let btncl = document.getElementById('btncl');
btn.addEventListener('click',throttle(dothing,2000,{
leading:false,
trailing:true
}));
</script>
应用实例:
飞机大战,飞机在移动,子弹隔一段时间射出
计算鼠标的移动距离