窗口的resize、scroll、输入框内容校验等操做时,若是这些操做处理函数是较为复杂或页面频繁重渲染等操做时,在这种状况下若是事件触发的频率无限制,会加剧浏览器的负担,致使用户体验很是糟糕。此时咱们能够采用debounce(防抖)和throttle(节流)的方式来减小触发的频率,同时又不影响实际效果。浏览器
在事件被触发n秒以后执行,若是在此期间再次触发事件,则从新开始计时。
咱们模拟输入表单数据,自动获取后台数据,联想搜索,反馈回前台。监听keyup事件,若是每次输入一个字符,就发送一次请求,那么将在极短的时间发送很是屡次请求,后台服务器不堪重负!服务器
// 监听input值 模糊搜索 防止一直搜索 <input id="phone" type="text"/>
// 须要触发的函数 function debounce(d){ console.log("联想搜索phoneNumber:" + d) } let inp = document.querySelector("#phone"); // 输入触发的事件 function getPhone(fn,delay){ let timer; // 使用闭包,保证每次使用的定时器是同一个 return (d)=>{ clearTimeout(timer); timer = setTimeout(()=>{ fn(d); // 结束以后清除定时器 clearTimeout(timer); },delay) } } let getPhoneDebounce = getPhone(debounce,1000); inp.addEventListener('keyup',(e)=>{ getPhoneDebounce(e.target.value); })
当即执行版的意思是触发事件后函数会当即执行,而后 n 秒内不触发事件才能继续执行函数的效果。
/** * @param func 函数 * @param wait 延迟执行毫秒数 * @param immediate true 表当即执行,false 表非当即执行 */ function debounce(func,wait,immediate) { var timeout; return function () { var context = this; var args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { var callNow = !timeout; timeout = setTimeout(function(){ timeout = null; }, wait) if (callNow) func.apply(context, args) } else { timeout = setTimeout(function(){ func.apply(context, args) }, wait); } } }
若是持续触发一个事件,则在必定的时间内只执行一次事件。
咱们模拟射击,首先第一次点击射击的时候,打出一发子弹,当以极短的时间再次点击射击的时候,因为须要‘冷却’——也就是节流,再次点击无效,当冷却时间过了以后,再次点击射击,则继续下一次射击
准备工具:一个射击的函数shot, 一个判断射击间隔是否结束的函数nextShot,一个触发射击的按钮,判断射击是否结束的定时器timer
基本思路:第一次点击按钮的时候,触发shot,当继续点击的时候,射击无效,只有过了定时器设置的时间才能够继续射击。闭包
// 模拟射击 <button id="shot">射击</button>
function shot(){ console.log('射击') } let btn = document.querySelector('#shot'); function nextShot(fn,delay){ let timer; // 闭包原理同上 return ()=>{ // 定时器存在,没法射击 if(timer){ console.log('禁止射击'); }else{ // 定时器不存在,射击,并设置定时器 fn(); timer = setTimeout(()=>{ // 定时器结束,能够射击 clearTimeout(timer); timer = null; },delay) } } } let start = nextShot(shot,20); btn.addEventListener('click',()=>{ start(); })