JavaScript:防抖和节流
一、防抖
-
定义:出发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次发生,则重新计算时间。
-
实现思路:每次触发事件时设置一个延迟调用方法,并且取消之前的延时调用方法;
-
缺点:如果事件在规定的时间间隔内不断被触发,则调用方法会不断地被延迟。
// 简易版
function debounce(fn){
let timeout = null;
return function(){
clearTimeout(timeout);
timeout = setTimeout(()=>{
fn.apply(this,arguments);
},1000);
}
}
function handle(){
console.log(this);
}
window.onmousemove = debounce(handle);
- 简易版有个缺点:当调用事件时,this对象永远指向window(因为在闭包中),因此,我们可以在闭包中先将上下文存储起来;
// 高级版
function debounce(func, wait, immediate){
var timeout, result;
let decounced = function(){
//改变内部this指向
let context = this;
let args = arguments;
clearTimeout(timeout);
if(immediate){
let callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait);
//表示立即执行
if(callNow) result = func.apply(context, args);
}else {
//不会立即执行
timeout = setTimeout(function(){
result = func.apply(context, args);
}, wait);
}
return result;
}
decounced.cancel = function(){
clearTimeout(timeout);
timeout = null;
}
return decounced;
}
function handle(){
console.log(this);
}
window.onmousemove = debounce(handle,1000,true);
二、节流
- 定义:高频事件触发,但是在n秒内只会执行一次,过亿节流会稀释函数的执行频率;
- 实现思路:当事件触发时,如果当前有等待执行的延时函数,直接return;
// 简易版
function throttle(fn){
let canRun = true;
return function(){
if(!canRun) return;
canRun = false;
setTimeout(()=>{
fn.apply(this,arguments);
canRun = true;
},1000);
}
}
function handle(){
console.log(Math.random());
}
window.onmousemove = throttle(handle);
- 同样的问题,简易版this指向window;
// 高级版
// options传入一个对象,表示模式
//{
// leading : true/false,
// trailing : true/false
//}
function throttle(fn,wait,options){
let context,args,timeout,result;
let old = 0;
if(!options) options = {
};
let throttled = function(){
context = this;
args = arguments;
let later = function(){
old = new Date().valueOf();
timeout = null;
result = fn.apply(context,this);
}
let now = new Date().valueOf();
if(options.leading === false && !old){
old = now;
}
if(now - old > wait){
//头部执行
if(timeout){
clearTimeout(timeout);
timeout = null;
}
result = fn.apply(context,args);
old = now
}else if(!timeout && options.trailing !== false){
//尾部执行
timeout = setTimeout(later,wait);
}
return result;
}
throttled.cancel = function(){
clearTimeout(timeout);
old = 0;
timeout = context = args = null;
}
return throttled;
}
function handle(){
console.log(Math.random());
}
window.onmousemove = throttle(handle,1000,{
leading : true,
trailing : false
});
三、区别
- 节流不管事件触发多么频繁,都保证在规定时间内一定会执行一次事件函数;
- 防抖只是在最后一次事件才触发一次函数;
四、适用场景
- 防抖
- scroll事件滚动触发,比如设置什么时候出现 回到顶部 的图标
- 搜索框输入查询,当用户输入完停顿时再进行搜索(模糊搜索)
- 表单验证,当用户输入完停顿时再进行实时检测
- 按钮提交事件,防止暴力点击发送多次请求
- 浏览器窗口缩放,resize事件
- 节流
- DOM元素的拖拽功能的实现
- 射击游戏
- 计算鼠标移动的距离
- 监听scroll滚动事件