window.addEventListener('resize',()=>{
sayHi('test')
})
function sayHi(name){
console.log(`hi ${name} `)
}
运行上面js代码,打开浏览器,改变窗口大小,会出现什么情况呢?改变窗口大小的过程中会触发若干次 resize 事件,会执行好多次 sayHi 回调函数。
因为 JS 中的一些事件会不断的调用事件绑定的回调函数,降低性能,为了优化体验,需要对这类事件进行调用次数的限制。
有以下两种不同类型限制回调函数调用次数的方法
防抖
在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
使用场景:resize 窗口大小调整事件,scroll 滚动事件,mousemove 鼠标事件,手机号、邮箱验证输入检测
function debounce(fn,delay){
let timer
return function(){
let _this = this
let args = arguments
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fn.apply(_this,args)
},delay)
}
}
let debounceSayHi = debounce(sayHi,1000)
window.addEventListener('resize',()=>{
debounceSayHi('lucy')
})
节流
在触发事件的n秒后会执行一次回调,在n秒内再触发事件的话不会执行回调。固定时间间隔执行。
使用场景:搜索框的联想功能,滚动加载
setTimeout 实现节流:
function throttle(fn,delay){
let timer
return function(){
if(timer){return}
let _this = this
let args = arguments
timer = setTimeout(()=>{
fn.apply(_this,args)
timer = null
},delay)
}
}
let throttleSayHi = throttle(sayHi,1000)
window.addEventListener('resize',()=>{
throttleSayHi('lucy')
})
利用时间戳实现节流
function throttle(fn, delay) {
var previous = 0;
return function() {
var _this = this;
var args = arguments;
var now = new Date();
if(now - previous > delay) {
fn.apply(_this, args);
previous = now;
}
}
}
对比
相同点:
- 都可以通过 setTimeout 实现
- 都可以降低性能,减少资源浪费
不同点:
- 在一段时间内连续触发事件的话,防抖是只会在最后执行一次;节流会按照固定时间间隔执行多次。