防抖 操作时不执行 确定不操作了才执行,在前端应用场景还是比较多的
好比你在一直玩手机的时候,手机不会息屏 玩了一分钟不玩了 就息屏了
原理: 操作(你快动我啊)=>关闭定时器(好了,我要重新数数了)=>重新开始计算器(你再不动我就要息屏了)=>执行(手机息屏了)
如何去实现这么一个函数
1.创建一个函数,他接受俩个参数,一个是你想去防抖的函数,一个是你想延时操作的时间
function debounce(fun,delay){
}
2.他返回一个自执行函数,并且这个函数在 delay后执行
function debounce(fun,delay){
return function(){
setTimeout(()=>{
fun()
},delay)
}
}
此时其实已经实现了一个延时函数了,但是需要自执行一下,因为我们返回的是函数体,可以使用debounce(fun,delay)(),或者return function(){}()加个括弧
function debounce(fun,delay){
return function(){
setTimeout(()=>{
fun()
},delay)
}
}
function sleep(){
console.log(1)
}
debounce(sleep,3000)()
那当然这种代码肯定没有如下来的简洁
const sleep = time =>{
return new Promise(resolve=>setTimeout(resolve,time))
}
3.再来看一遍原理 操作=>关闭定时器=>重新开始计算器=>执行
,那么接下来就是对定时器的操作,我们声明一个定时器然后并覆盖
function debounce(fun,delay){
let timer
return function(){
if(timer) clearTimeout(timer)
timer = setTimeout(()=>{
fun()
},delay)
}
}
那么到这步,其实已经实现了大多数的防抖,我们尝试一下
function watchResize() {
console.log(1)
}
window.addEventListener('resize', debounces(watchResize, 1000))
在页面中进行窗口改变的时候,当你停止改变1s后就会打印1,当然这个函数还有一些优化,比如对fn的类型判断,delay的处理等等,了解基础原理之后 稍微进阶一下
<input type="text" id="ipt">
比如我们写个input,给它绑定一个input事件
ipt.oninput = function(){
console.log(this.value)
}
OK,此时没有问题,但是我们想要用户最后输入的值,做个防抖
ipt.oninput = debounce(function () {
console.log(this.value)
}, 2000)
此时控制台你会发现,打印的是undefined,问题就出在this指向问题
ES5: 一个函数在被调用时,会自动取得两个特殊变量:this和arguments。在全局环境调用函数时,this代表window对象;而当函数被作为其他某个对象的方法而调用时,this就代表那个对象。
简而言之: ES5的函数中,this是执行时函数所在的那个对象。
function debounce(fun, delay) {
console.log(this) //此处打印的是windows
let timer
return function () {
//此处是执行环境 因为返回的函数 在xx环境下触发的 xx就是调用的对象
console.log(this)
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fun()
}, delay)
}
}
所以我们在定时器的函数执行显示绑定当前的this即可
function debounce(fun, delay) {
let timer
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fun.apply(this)
}, delay)
}
}
同理,调用的时候就不能使用箭头函数,箭头函数不存在this,当然你非要用 ,可以这样用
const co = (obj)=>obj.value
ipt.oninput = debounce(function(){
console.log(co(this))
},1000)
感觉就挺憨的!!
节流,原理差不多,都是操控时间
好比你打游戏的技能冷却,亦或是你等的公交车,总是多少时间来一趟,如何控制时间有俩种说法,可以通过当前时间减去上一次时间,也可以通过设置的时间去操作,两种思路都可以
我们先按照防抖的思路写一个setTimeout版本,老样子 先返回一个函数,有了前面的基础,现在节奏快点
function throttle(fn,delay){
return function(){
setTimeout(()=>{
fn.apply(this,arguments)
},delay)
}
}
然后我们去做一下控制
function throttle(fn, delay) {
//开启一个值 默认关闭
let timer = false
return function () {
//如果这个timer是开启的 那么说明我们在时间范围之外 那我们不做啥 直接返回
if (timer) {
return
}
else {
//开启这个值
timer = true
setTimeout(() => {
fn.apply(this, arguments)
//说明下一次可以执行了 那你就继续false吧
timer = false
}, delay)
}
}
}
window.addEventListener('resize', throttle(watchresize, 2000))
然后你发现在一直操作窗口的时候,2s触发一次咯
function throttle(fun, delay) {
let previous = 0;
return function () {
let now = Date.now();
let args = arguments;
if (now - previous > delay) {
func.apply(this, args);
previous = now;
}
}
}
也可以使用时间戳去控制!! 看完尝试自己再手写几遍就应该可以理解了