debounce的特点是当事件快速连续不断触发时,动作只会执行一次。
周期内有新事件触发,清除旧定时器,重置新定时器;这种方法,需要高频的创建定时器。
简单实现:
<button id="btn" style="width: 100%;">按钮</button>
const button = document.querySelector('button')
function payMoney() {
console.log('滴滴滴');
console.log(this); // 不使用防抖函数this指向点击按钮
// 使用防抖函数this指向window
}
function debounce(fun,time) {
let timer;
return function() {
let _this = this;
let args = arguments
clearTimeout(timer)
timer = setTimeout(function() {
fun.apply(_this,args)
},time)
}
}
button.addEventListener('click',debounce(payMoney,1000))
/*
function ebounce(func) {
func()
}
难点1: 这样写的话,在定义监听函数的时候就直接执行了函数
为了解决这个问题,使用高阶函数,在函数里面返回函数
function ebounce(func) {
return function() {
func()
}
}
要进行延迟就相应的需要清除延时,为延时设置变量名,清除是使用 clearTimeout
难点2: 清除以后在定义变量是会报错的
难点3: 1秒内多次执行,但是全部消息都陆续执行了
这里每次执行函数就是创建变量,清除延时,建立延时三个步骤;
而且每次点击的执行函数都是独立的,因为他们直接没有联系,
因此清除延时在这里完全不起作用;
要让他们直接有联系就需要利用作用域链了,也就是闭包;
把定义timer放到函数外
难点4: this指向
参数问题: js里函数没有设置参数是可以传入参数的
每一个执行函数有可能传入参数,而传入的参数是给payMoney函数使用的
*/
封装一下:
<button id="btn" style="width: 100%;">按钮</button>
const button = document.querySelector('button')
function payMoney() {
console.log('滴滴滴');
console.log(this); // 不使用防抖函数this指向点击按钮
// 使用防抖函数this指向window
}
function debounce(fun,wait) {
let timer;
let timerStamp;
let _this;
let args;
let clear= () => {
clearTimeout(timer)
}
let run = () => {
timer = setTimeout(function() {
fun.apply(_this,args)
},wait)
}
return function(){
_this = this
args = arguments
let now = new Date().getTime()
if(now-timerStamp < wait){
clear()
}
run()
timerStamp = now
}
}
button.addEventListener('click',debounce(payMoney,1000))
throttling,节流的策略是,固定周期内,只执行一次动作,若有新事件触发,不执行。周期结束后,又有事件触发,开始新的周期。
throttling的特点在连续高频触发事件时,动作会被定期执行,响应平滑。
小实验:
f function coloring() {
let r = Math.floor(Math.random() * 225);
let g = Math.floor(Math.random() * 225);
let b = Math.floor(Math.random() * 225);
document.body.style.background = `rgb(${r},${g},${b})`
}
function throttle(fun,delay) {
let timer;
return function() {
let _this = this;
let args = arguments
if(timer){
return
}
timer = setTimeout(function() {
fun.apply(_this,args)
timer = null
},delay)
}
}
window.addEventListener('resize',throttle(coloring,1000))
/*
判断触发的事件是否在时间间隔内,如果在时间间隔内,不触发事件,如果不在,就出发事件.
如果timer被赋值,也就是任务在等待执行,暂时不改变timer的值,如果没有值,就直接赋值
if timer有值,直接返回, 没有值,进行赋值
*/
第二种写法:
function coloring() {
let r = Math.floor(Math.random() * 225);
let g = Math.floor(Math.random() * 225);
let b = Math.floor(Math.random() * 225);
document.body.style.background = `rgb(${r},${g},${b})`
}
function throttle(fun,delay) {
let pre = 0;
return function() {
let _this = this;
let args = arguments
let now = new Date()
if(now - pre > delay){
// 当前时间减去上一次执行的时间,如果大于传入的时间就执行,否则不执行
setTimeout(function() {
fun.apply(_this,args)
pre = now
})
}
}
}
window.addEventListener('resize',throttle(coloring,1000))
封装1:
var throttle = (fn, wait) => {
let timer;
let context, args;
let run = () => {
timer=setTimeout(()=>{
fn.apply(context,args);
clearTimeout(timer);
timer=null;
},wait);
}
return function () {
context=this;
args=arguments;
if(!timer){
console.log("throttle, set");
run();
}else{
console.log("throttle, ignore");
}
}
}
window.addEventListener('resize',throttle(coloring,1000))