今年5月份毕业出来找工作的时候听说的函数防抖与节流,拖到现在采取整整的整理了解,之前也看过几篇关于函数节流和防抖的文章,但是作者都没好好检查下代码就把报错的代码发上来,所以今天 特意花点时间自己去好好了解函数防抖、函数节流,也遇到一些小坑,分享给大家,也作为自己的笔记。
首先要搞明白为什么要用函数防抖 函数节流,因为在开发时,有些操作调用函数十分的频繁,比如 resize、scroll、mousemove
我这边用的是mousemove事件做的例子
0//html let span = document.getElementById('span')//js let num = 0 function mouseover (...args){ console.log('mouseoverz执行了') num++ this.innerHTML = num }复制代码
函数防抖 debounce
当你连续触发函数的时候,函数只会被触发一次。主要有以下两种
1.非立即执行版
//非 立即执行版 const debounce = (func,wait,...args)=>{ let timeout; return function () { let context = this; if(timeout) clearTimeout(timeout); timeout = setTimeout(()=>{ func.apply(context,args); }, wait) } }span.onmouseover = debounce(mouseover,1000,'a','b','c')复制代码
每次经过数字都会产生一个新的计时器,清除旧的计时器,直到你在1秒内不执行这个函数,目标函数才会被调用
在写这个函数的时候也发现了箭头函数和普通函数的一些区别
//setTimeout 回调函数为非箭头函数 const debounce = (func,wait,...args)=>{ let timeout return function (e){ const context = this if(timeout) clearTimeout(timeout); timeout = setTimeout(function(){//setTimeout 返回值是这个额计时器一个唯一标志符 对于延时函数内部的回调函数的this指向全局对象window console.log(this)//window 有点意外吧 setTimeout是window的方法所以this指向window console.log(context)//这个好理解,谁去调用就指向哪个 func.apply(context,args)//apply能够将数组自动转化为参数列表 }, wait) } } //setTimeout 回调函数为箭头函数 const debounce = (func,wait,...args)=>{ let timeout return function (e){ const context = this if(timeout) clearTimeout(timeout); timeout = setTimeout(()=>{//setTimeout 返回值是这个额计时器一个唯一标志符对于 延时函数内部的回调函数的this指向全局对象window console.log(this)//由于这里的函数时箭头函数,箭头函数的this指向的是定义时的this,而不是执行的this, 所以这里的this会和context保持一致 console.log(context) func.apply(context,args)//apply能够将数组自动转化为参数列表 }, wait) } }span.onmouseover = debounce(mouseover,1000,'a','b','c')复制代码
2.立即执行版
const debounce = (func,wait,...args)=>{ let timeout; return function (){ let context = this if(timeout) clearTimeout(timeout); if(!timeout) func.apply(context,args); timeout = setTimeout(()=>{ timeout = null }, wait) } }复制代码
每次经过数字都会产生一个新的计时器,清除旧的计时器,如果timeout为null 那么就会立即执行目标函数 必须间隔1秒后划过数字,函数才能再次被触发
结合版
// 结合版 const debounce = (func,wait,blo,...args) => { let timeout; return function () { let context = this; if(timeout) clearTimeout(timeout); if(blo){//立刻执行版 if(!timeout) func.apply(context,args) timeout = setTimeout(()=>{ timeout = null }, wait) }else{//非 立刻执行版 timeout = setTimeout(()=>{ func.apply(context,args) }, wait) } } } span.onmouseover = debounce(mouseover,1000,false,'a','b','c')复制代码
2.函数节流 throttle
函数节流:你连续触发某个函数,你可以控制这个函数多久触发一次 ,而函数防抖实际上是你连续触发某个函数,最终只会触发一次,你需要停止触发,过一段时间再去触发,才能再次触发成功
第一种:时间戳版
// 时间戳版 //连续触发时 第一次会立即执行,之后秒执行一次 const throttle = (func,wait,...args) => { let previous = 0; return function () { let context = this; let now = Date.now() if(now - previous > wait){ func.apply(context,args) previous = now } } }复制代码
这里应该很清楚,不需要解释
第二种:计时器版
// 计时器版 //连续触发时 第一次不会立即执行,一秒后秒执行一次 所以会出现你停止不滑动后,数字最后跳了一次 const throttle = (func,wait,...args)=>{ let timeout; return function () { let context = this; if(!timeout){ timeout = setTimeout(()=>{ timeout = null; func.apply(context,args) },wait) } } }复制代码
节流结合版
这是我一开始写的,其实是有问题的,你能发现吗
// 节流的结合版 const throttle = (func,wait,type,...args) => { console.log(type === 1) if(type === 1){//时间戳版 let previous = 0// }else if(type === 2){//定时器版 let timeout } console.log(timeout) return function () {//时间戳版 let context = this if(type === 1){ let now = Date.now(); if(now - previous > wait){ func.apply(context,args) previous = now } } if(type === 2){//定时器版 if(!timeout){ timeout = setTimeout(()=>{ timeout = null; func.apply(context, args) }, wait) } } } } span.onmouseover = throttle(mouseover,1000,1,'a','b','c')复制代码
看了报错,才找到的原因
// 节流的结合版 const throttle = (func,wait,type,...args) => { console.log(type === 1) if(type === 1){//时间戳版 var previous = 0//这里不能用let 因为有块级作用域,return出去function不能访问,所以要用var }else if(type === 2){//定时器版 var timeout } console.log(timeout) return function () {//时间戳版 let context = this if(type === 1){ let now = Date.now(); if(now - previous > wait){ func.apply(context,args) previous = now } } if(type === 2){//定时器版 if(!timeout){ timeout = setTimeout(()=>{ timeout = null; func.apply(context, args) }, wait) } } } } span.onmouseover = throttle(mouseover,1000,1,'a','b','c')复制代码