前言
本篇章主要讲述防抖、节流的原理和应用场景,并且手动实现防抖、节流函数。
面试回答
1.防抖节流:防抖是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段事件执行。防抖触发高频率事件时n秒后只会执行一次,如果n秒内再次触发,则会重新计算。节流触发高频事件,相当于控制流量,它是在指定的单位时间内触发一次。可以通过时间戳来设置间隔,如果小于间隔就不执行,大于间隔就执行。也可以通过定时器来实现,如果存在定时器就不执行,如果不存在就执行并清空定时器。
知识点
节流和防抖的区别在于,如果当前有等待执行的延时函数时,防抖是创建一个新的延时函数去替换旧的,节流是直接return,等待旧的函数执行完毕。
1.防抖
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。
思路:每次触发事件时都取消之前的延时调用方法
实例场景:input输入框,输入后查询接口获得下拉框,希望输完后只发一次请求
基础实例:
function debounce(fn) {
let timeout = null; // 创建一个标记用来存放定时器的返回值
console.log('1-------------',timeout)
return function () {
console.log('2-------------',timeout)
if(timeout){
clearTimeout(timeout);
// 每当用户输入的时候把前一个 setTimeout clear 掉
}
timeout = setTimeout(() => {
// 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn.apply(this, arguments);
}, 2000);
};
}
function sayHi() {
console.log('防抖成功');
}
window.addEventListener('resize', debounce(sayHi));
//这边应用了函数柯里化,即利用函数执行可以形成一个不销毁的私有作用域(timeout),把预先处理的内容都存在这个不销毁的作用域里面(除了第一次外不再执行let timeout = null ),并且返回一个小函数,以后要执行的就是这个小函数。
//首先,return的函数会直接执行
//其次,当触发resize时,会直接执行return的函数,而没有再执行let timeout = null
优化实例:
data(){
return {
inputContent:'', //input内容
inputSearch:'', //input下拉框内容
timeout:null //用来存放定时器的返回值
}
},
methods:{
debounce(){
if(this.timeout){
clearTimeout(this.timeout)//每当用户输入的时候把前一个setTimeout 清除掉
}
//然后又新建一个setTimeout,这样就能保住输入字符后的setTimeout间隔内如果还有字符输入的话,就不会执行requestMethods方法
this.timeout = setTimeout(()=>{
this.requestMethods()
},500)
//输入第一个字符的时候返回一个新的延迟函数,保存在this.timeout中,这个延迟函数在满足延迟时间后,会执行传requestMethods
//输入第二个字符的时候this.timeout已经有延迟函数(如果在500ms内),这时候把旧的延迟函数清除掉,再添加一个新的延迟函数
//输入第三个字符的时候重复如上步骤,直到最后一次输入,500ms延迟后,回调searchData方法
},
searchData(){
this.debounce()
//@input="searchData",每次输入字符都调用这个方法
},
requestMethods(){
//getData为请求方法
getData(this.inputContent).then(res=>{
this.inputSearch = res.data.inputSearch
})
}
}
例子:
// html 部分同上
// js 部分
let debounce = function (fn, wait) {
let timeout = null;
return function () {
if (timeout !== null) clearTimeout(timeout);//如果多次触发将上次记录延迟清除掉
timeout = setTimeout(() => {
fn.apply(this, arguments);
// 或者直接 fn()
timeout = null;
}, wait);
};
}
// 处理函数
function handle() {
console.log(arguments)
console.log(Math.random());
}
// 测试用例
document.getElementsByClassName('scroll-box')[0].addEventListener("scroll", debounce(handle, 3000));
2.节流
高频事件触发,但在n秒内只会执行一次,如果n秒内再次触发事件,则直接return,所以节流会稀释函数的执行频率。
思路:每次触发事件时都判断当前是否有等待执行的延时函数
实例:
function throttle(fn) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
canRun = false; // 立即设置为false
setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
canRun = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
//这边应用了函数柯里化,即利用函数执行可以形成一个不销毁的私有作用域(canRun),把预先处理的内容都存在这个不销毁的作用域里面(除了第一次外不再执行let canRun = true ),并且返回一个小函数,以后要执行的就是这个小函数。
//首先,return的函数会直接执行
//其次,当触发resize时,会直接执行return的函数,而没有再执行let canRun = true
例子:
// html 部分
<style>
*{padding:0;margin:0;}
.scroll-box{
width : 100%;
height : 500px;
background:blue;
overflow : auto;
}
.scroll-item{
height:1000px;
width:100%;
}
</style>
<body>
<div class="scroll-box">
<div class="scroll-item"></div>
</div>
</body>
// js 部分
let throttle = function (func, delay) {
let timer = null;
return function(){
if (!timer) {
timer = setTimeout(() => {
func.apply(this, arguments);
// 或者直接 func()
timer = null;
}, delay);
}
};
};
// 处理函数
function handle() {
console.log(arguments)
console.log(Math.random());
}
// 测试用例
document.getElementsByClassName('scroll-box')[0].addEventListener("scroll", throttle(handle,3000));
最后
走过路过,不要错过,点赞、收藏、评论三连~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。