今年5月份毕业出来找工作的时候听说的函数防抖与节流,拖到现在采取整整的整理了解,之前也看过几篇关于函数节流和防抖的文章,但是作者都没好好检查下代码就把报错的代码发上来,所以今天 特意花点时间自己去好好了解函数防抖、函数节流,也遇到一些小坑,分享给大家,也作为自己的笔记。
首先要搞明白为什么要用函数防抖 函数节流,因为在开发时,有些操作调用函数十分的频繁,比如 resize、scroll、mousemove
我这边用的是mousemove事件做的例子
<span id="span">0</span>//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')