函数防抖和节流
在前端开发过程中,经常遇到一些持续触发的事件,如 resize、scroll、mousemove、键盘事件等,但有些时候不希望这类事件持续触发。
因此,防抖和节流就是解决这类需求应运而生的。
以键盘事件为例,一般除非要求输入模糊匹配时,要求实时获得输入框的值(见案例:https://www.cnblogs.com/danew/p/11530402.html);
但平时输入框带验证时,你会经常遇到刚打一个字就提示输入格式不正确,红色的提醒很醒目,但是也逼死强迫症,我刚输入邮箱的第一位就弹我?难道我邮箱都填不对?就不能等我填完再提醒是否正确? (素质三连...)
其判断原理是每次键盘事件都获取value并执行一次校验,所以及时是及时了(比较占用性能),但用户体验不太好,有时候不需要这么及时,上代码简单分析一下这类事件的原理:
<div id="wrap"> <input id="key" type="text"> <p id="content"></p> </div> <script> let key = document.getElementById('key'); let content = document.getElementById('content'); key.onkeyup=show; function show() { content.innerHTML+=this.value+'<br/>'; // 每次触发事件,都获取一次 value 值 } </script>
然后是效果图!(最喜欢贴图了,排版好看点...)
那怎么用防抖改良这个代码呢?其实,防抖的效果就是让你在输入的时候,不执行下一步,等你输入完成后,在键盘被释放了一小段的间隔后,执行下一步操作(比如执行校验)。
<div id="wrap"> <input id="key" type="text"> <p id="content"></p> </div> <script> let key = document.getElementById('key'); let content = document.getElementById('content'); key.onkeyup=debounce(show,1000); function show() { content.innerHTML+=this.value+'<br/>'; } //我叫函数防抖 function debounce(func,delay) { let timer = null; return function () { let context = this; //必须保存事件的this let args = arguments; //参数也要保存 if(timer){ clearTimeout(timer); //事件再次触发时,那就取消之前的定时事件 } timer = setTimeout(()=>{ func.call(context,...args); // 其实用的比较多的是apply,直接传参数组 // 这里我就用call,据说call的性能比apply要好点(有大佬测试过),特别是参数大于3个的时候,如果参数<=3,两个五五开 },delay) } } </script>
好了,上效果图!
输入邮箱还不是很快的么,这里只有等键盘释放超过1s,才能执行对应的绑定事件(可以执行校验,此处省略),如果释放时间未到,则会取消上一次的定时器,重新计时。
所以事件防抖有点像打游戏,释放大招需要1s,但你可以通过按其他的键或是走位,重置读秒,取消大招,防止误伤队友或是空放技能;也可以一直按着按键不放(不断重置cd),等瞄准了再松开,然后放出大招。
那有人问了:我想走A怎么办?或者站撸一直普A行不行?那就请看看节流!
节流与防抖不同,当你一直按着按键时,节流会定时执行一次绑定事件。就是说,你装备比较好,可以对着BOSS站撸,一直按着F键,攻击会定时触发,然后全程只需按着F键,就能杀死BOSS!
上代码!
<div id="wrap"> <input id="key" type="text"> <p id="content"></p> </div> <script> let key = document.getElementById('key'); let content = document.getElementById('content'); key.onkeypress=throttle(show,1000); //注意,这里 keypress 事件才支持一直按 function show() { content.innerHTML+="我普A了BOSS一次!"+'<br/>'; } // 我叫节流,能定时攻击你的那个 function throttle(func,delay) { let timer = null; return function () { const context = this; //保存事件的触发对象 let args = arguments; if(!timer){ //必须确保上一次定时器执行完毕 timer = setTimeout(()=>{ func.call(context,...args); timer=null; //及时清理,表示执行完毕, clearTimeout后timer仍有值!!!这里有坑!画重点!!! },delay) } } } </script>
好了,上效果图!
总结:
防抖:触发事件后必须等待一个delay才能执行,频繁触发只会重置等待时间。打游戏可以通过连续按键用来重置技能读秒,或延迟释放技能。
节流:允许你频繁触发事件,但我只会按我的节奏(delay周期)来执行事件。打游戏一直按着按键一直普A,可以省得频繁操作按键(懒人打法,人民币玩家打法)。