函数节流和函数防抖,两者都是优化高频率执行js代码的一种手段。 大家大概都知道旧款电视机的工作原理,就是一行行得扫描出色彩到屏幕上,然后组成一张张图片。 由于肉眼只能分辨出一定频率的变化,当高频率的扫描,人类是感觉不出来的。反而形成一种视觉效果,就是一张图。就像高速旋转的风扇,你看不到扇叶,只看到了一个圆一样。 同理,可以类推到js代码。在一定时间内,代码执行的次数不一定要非常多。达到一定频率就足够了。因为跑得越多,带来的效果也是一样。 倒不如,把js代码的执行次数控制在合理的范围。既能节省浏览器CPU资源,又能让页面浏览更加顺畅,不会因为js的执行而发生卡顿。这就是函数节流和函数防抖要做的事。
函数节流是指一定时间内js方法只跑一次。比如人的眨眼睛,就是一定时间内眨一次。这是函数节流最形象的解释。 函数防抖是指频繁触发的情况下,只有足够的空闲时间,才执行代码一次。比如生活中的坐公交,就是一定时间内,如果有人陆续刷卡上车,司机就不会开车。只有别人没刷卡了,司机才开车。
一般我们在 input 输入框中输出值,并且向服务端查询数据的时候,如果每次 input 值发生改变,都向服务器发请求的话,难免会资源浪费,并且用户体验并不好,那么我们可以在 input 输入值后延迟一段时间,再发请求。就是 防抖
<body>
<input type="text" id="text">
<script>
document.getElementById('text').oninput = function () {
search()
}
function search () {
console.log('发起请求')
}
</script>
</body>
上面代码,我们可以得到, 每次 input 的值发生改变,都会输出 发起请求, 为了调整发起请求的频率,我将代码调整这样
<body>
<input type="text" id="text">
<script>
const text = document.getElementById('text')
text.oninput = debounce(search, 2000)
function search() {
console.log('发起请求')
}
function debounce(fn, delay) {
let timer
return function (...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
</script>
</body>
//当 input 输入一个值的时候,间隔 2000ms 才输出一个 发起请求, 如果我们连续输入, 只会输出一个 发起请求
搜索引擎边输入边联想是怎么实现的?
self.bindEventAboutGrid($("#SE1G040_confirm_button"), $.proxy(throttle(this.confirm, 1000), this), dm, true);
函数节流的基本思想是设置一个定时器,在指定时间间隔内运行代码时清楚上一次的定时器,并设置另一个定时器,知道函数请求停止并超过时间间隔才会执行
//节流函数,控制事件在一定时间内只发生一次, 防止重复请求, 影响性能
var throttle = function (fn, delay) { //参数fn为要执行的函数, delay为时长
var timer = null;
return function () { //通过闭包来实现。首先在函数内定义一个变量timer,然后返回一个函数,我们知道由于在返回函数中包含对timer的引用,因此timer并不会立即被清除。(我们也可以将timer定义为全局变量)
clearTimeout(timer);
timer = setTimeout(function () {
fn();
}, delay);
}
};
function throttle(method,delay){
var timer=null;
return function(){
var context=this, args=arguments;
clearTimeout(timer);
timer=setTimeout(function(){
method.apply(context,args);
},delay);
}
}
function throttle(method, context){ //参数method为要执行的函数, context为执行环境,如果执行环境未定义,默认则为windows
clearTimeout(method.timer);
method.timer=setTimeout(function(){ //将定时器设置为函数的一个属性
method.call(context);
}, 300)
}
以上两种方案存在一个问题,即如果事件一直触发,那么函数将永远不会被执行,这在某些时候并不符合我们的需求,可能我们只是想在规定时间内减少函数执行次数,所以对以上函数做如下改进:
function scrollFn(){
console.log(1)
}
function throttle(method,delay,duration){
var timer=null;
var begin=new Date();
return function(){
var context=this, args=arguments;
var current=new Date();
clearTimeout(timer);
if(current-begin>=duration){
method.apply(context,args);
begin=current;
}else{
timer=setTimeout(function(){
method.apply(context,args);
},delay);
}
}
}
window.onscroll=throttle(scrollFn,100,500)
在初始的时候定义一个begin开始时间,当时间间隔超过duration时,则执行一次函数,这样做到了不重复调用,又能保证500秒执行一次