防抖和节流 && 手写js防抖和节流函数
防抖(debounce)
顾名思义:抖就是手抖,手抖会造成什么,造成点击某个窗口同一地方短时间内点击多次,具体点击多少次就看你手抖的程度了。 防抖就是为了避免这种情况的发生,保证这个函数在短时间内只执行最后一次。
实现原理: 函数被触发时,先延迟执行的时间,在延迟的时间内如果再次被触发,则取消之前的延迟,再重新开始新的一轮延迟,周而复始,达到只执行最后一次请求,其他请求则被过滤掉的效果。
手动实现一个防抖函数:
//fn为高频出发的函数,delay为延迟的时间
function debounce(fn,delay){
var timer = null
return function(){
//清除之前的定时器
clearTimeout(timer)
//重新设置一个新的定时器
timer = setTimeout(()=>{
fn.call(this)
},delay)
}
}
//自定义一个函数a
function a(){
console.log("hello yu")
}
//调用防抖函数
var b = debounce(a,3000)
for(let i=1;i<10;i++){
b()
}
我本人看到这段代码的时候有两个疑惑点 1.var timer = null这句和clearTimeout(timer)两个不是冲突了吗,都初始化成null,为啥还清除。 2.fn.call(this)这是个啥玩意,直接fn()不行吗?
经过一番操作,才知其中关窍 1.我们的b()是指debounce函数返回的结果,也是一个函数,具体指的就是
return function(){
//清除之前的定时器
clearTimeout(timer)
//重新设置一个新的定时器
timer = setTimeout(()=>{
fn.call(this)
},delay)
}
这个函数,就意味着var timer = null这句只执行一遍。
2.第二个问题是因为之所以要通过call方式来修改原函数的this,是因为原函数在通过参数传递时,只会被当成普通函数处理,不会去管原函数是否挂载在某个对象上,如果说原函数内部有this指向,如果直接使用fn(),会出现this指向错误的问题。
针对这个问题有两种解决办法
1.传递fn()原函数时手动进行显示绑定this
function debounce(fn,delay){
var timer = null
return function(){
//清除之前的定时器
clearTimeout(timer)
//重新设置一个新的定时器
timer = setTimeout(()=>{
fn()
},delay)
}
}
var o = {
c: 1,
a: function() {
console.log(this.c);
}
}
var b = debounce(o.a.bind(o));
2.就在debounce函数内部通过apply或者call的方式调用原函数 但是使用这个方法有个前提就是如果原函数本来挂载在某个对象上,return的函数也需要挂载在那个对象上,这样才会保证this是同一个this
function debounce(fn,delay){
var timer = null
return function(){
//清除之前的定时器
clearTimeout(timer)
//重新设置一个新的定时器
timer = setTimeout(()=>{
fn.call(this)
},delay)
}
}
var o = {
c: 1,
a: function() {
console.log(this.c);
}
}
var b = debounce(o.a);
这个地方如果写成var b = debounce(a)也会发生this指代错误。
节流(throttle)
节流和防抖本质上解决的问题是一样的,只是处理的方法是不一样的
实现原理: 函数被触发时,只响应在某个固定时间短内的第一次请求,后续的请求都不响应,直到下个时间周期,继续响应下个周期内的第一次请求,周而复始。
上代码:
//节流函数
function throttle(fn,delay){
var lastTime = 0
return function(){
var nowTime = new Date().getTime()
console.log(lastTime)
if(nowTime - lastTime > delay){
fn.call(this)
lastTime = nowTime
}
}
}
//自定义一个函数a
function a(){
console.log("hello yu")
}
//调用节流函数
var b = debounce(a,3000)
for(let i=1;i<1000;i++){
b()
}
是不是很简单呢~
