0前言:
节流和防抖是前段优化的手段之一。他的作用是减少频繁触发,例如用户搜索智能提示中,如果输入每一个字都要发起后台请求对服务器端的压力太大。
本文介绍了防抖和节流的基本原理,实现方式,他是高阶函数的一个应用。
1 防抖:debounce。
在介绍前,先看一下事件监听addEventListener()方法
语法: element.addEventListener(event,function,useCapture)
第一个参数是事件类型,不要加on
第二个参数是事件触发后调用的函数
第三个参数是个布尔值用于描述事件是冒泡还是捕获。该参数是可选的默认值为false
定义input输入框: <input type="text" id='input' value="输入:">
// 定义一个函数e 监听他的输入,当输入时打印输入的值 function f(e) { console.log(Date.now(),e.target.value) } document.getElementById('input').addEventListener('input',function(e){ f(e) //监听input输入,触发调用f函数 })
1.1 那么,如何让他打印的频率慢一点?
防抖-立即执行版本 :例:你从8点一直输入,到12点松手,那么只会打印一次。
实测不好的地方是,当我停止输入,他1秒打印一次了。(最后一个定时器没清除,一直执行)
代码:加入定时器,设置延时,这样就不会频繁的触发了。
function f(e) { this.clearInterval(this.timer) //代码进入函数体,先结束之前的定时器 this.timer = setInterval(()=>{ // 要执行的函数 一秒执行一次, console.log(Date.now(),e.target.value) //因此同一时间下,只有最后一个定时器执行 },1000) } document.getElementById('input').addEventListener('input',function(e){ f(e) })
防抖-非立即执行版本 :例:假如你设置点击后3秒后打印,3秒内又点击了按钮,则重新计时。直到你点了然后过了3秒,才执行(算是上面的优化版)
---我继续用上面的例子,效果:当我停止输入3秒后才打印,如果我输入一直不停,就一直不打印。
function f() { this.clearInterval(this.timer) //结束之前的定时器 let a = document.getElementById('input').value //拿input中value的值 let oop = setInterval(()=>{ // 要执行的函数 //设置最后一个定时器的id叫oop console.log(Date.now(),a) return },2000) setInterval(()=>{ // 要执行的函数 this.clearInterval(oop) //2秒后清除掉最后一个定时器 },2000) } document.getElementById('btn').addEventListener('click',function(){ f() })
这个的效果是点击按钮后2秒,打印一下,然后2秒后结束最后一个定时器,后台不会继续打印了,当我再点的时候2秒后继续打印,再2秒后停止定时器。
防抖-混合版本:我想点击按钮的时候,先发一次,然后再点的话2秒后才打印,这个步骤比起上面的就多了一个第一次点要打印一次。
解决:设置一个变量,对他判断,如果变量是true就执行打印内容,然后定时器里面把它设置成null(布尔值是false),就可实现
function f(t) { let a = document.getElementById('input').value if(t){ console.log(Date.now(),a) } let oop = setInterval(()=>{ // 要执行的函数 console.log(Date.now(),a) // console.log(a) t=null return },2000) setInterval(()=>{ // 要执行的函数 this.clearInterval(oop) },2000) } document.getElementById('btn').addEventListener('click',function(t=1){ f(t) })
2节流:throttle
通俗理解:函数节流就是fps游戏的射速:就算一直按着鼠标射击,也只会按规定射速射出子弹(不会无限速度发子弹的)。单位时间内最高的发送频率,控制频率
由于代码及其相似就不写了,其实这个更简单,只需点击之后设置定时器,2秒,他就会每隔2秒打印一次。
3总结
如果事件触发是高频但是有停顿时,可以选择debounce; 在事件连续不断高频触发时,只能选择throttling,因为debounce可能会导致动作只被执行一次,界面出现跳跃。
•相同点:函数防抖和函数节流都是防止某一时间高频调用某函数f,而使用高阶函数的技巧对函数f进行包装以得到新函数f1,f1的功能与f相同,只是在有效调用上做了限制;
•不同点:函数防抖是某一段时间内只有效执行一次(或者两次吧),而函数节流是间隔一段时间有效执行一次。• 本文只是基本实现其原理,对参数和上下文并没有处理。建议使用underscore库中的工具方法:节流[1] ,防抖[2]
推荐记忆方案:记住一个好理解的,另一个对比。
•节流是节约用水:把一直连续放水的水龙头关小,直到让水是一点一点向下滴(函数还是可以被调用多次,只不过是频率变慢了)。其实你只要记住这一个,就能区别于防抖啦。
4.区别
相同点:降低处理程序的频率
不同点:
防抖:某个时间内不能再次触发,一旦触发,就要重新计时(例如:等电梯)
节流:限制相邻两次调用的时间间隔。(例如:调小水龙头)
4题目
前提,在网页中,从顶部向底部匀速,连续不断地滚动滚动,给添加scroll事件监听时,并对回调函数进行节流处理和防抖处理,如果时间都给1s,
补充知识点:不同浏览器获取页面滚动距离的方法不一样
(window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop)
document.documentElement.scrollTop这个案例中使用这个获取成功了。
function f(){ console.log(Date.now() } var f1 = 防抖(f,1000) var f2 = 节流(f,1000) window.onscroll = f2;// 或者f1
它们展现的效果会有什么区别?
答案:
先说节流:1.如果初始的滚动条不在0,那么他会一秒输出一次初始位置的距离。
2.如果初始位置在0,拖动后,每隔一秒输出很多次位置。一直匀速拖动的话,位置输出次数不固定,但稍微停顿便会输出很多次
防抖:1.如果初始的滚动条不在0,那么他只会输出一次位置。
2.如果初始位置在0,拖动后,每隔一秒输出很多次位置。一直匀速拖动的话,位置输出次数不固定,但稍微停顿便会输出很多次(几乎无区别)
什么时候使用防抖,什么时候使用节流?
① 当我们只需要处理最后一次触发事件时,用函数防抖。
② 当事件触发过于频繁,我们需要限制事件处理程序的调用频率时,用函数节流