解决当函数在某些场景下被无限制的频繁调用,会增长浏览器的负担,会形成卡顿的现象浏览器
鼠标滚动、键盘输入操做、窗口resize等等闭包
防抖:事件持续触发时,若是间隔时间小于预设的时间,不会执行函数,同时以最后触发事件的时间为准从新计时。总之,必定要在事件触发完的预设时间内没有触发过,才会执行函数app
手写+逐步完善函数
function _debounce(func, wait){ // 定义一个计时器 var timer return function(){ // 拿到当前的执行上下文 var context = this clearTimeout(timer) timer = setTimeout(function(){ // 由于setTimeout回调方法中this永远是window,因此~ func.apply(context) },wait) } }
以上实现方式有一个不足,就是当事件处理函数传入参数(event对象)时,没有作到获取,修改一下:优化
function _debounce(func, wait){ var timer return function(){ var context = this // 经过arguments拿到调用时所传入的参数 var arg = arguments clearTimeout(timer) timer = setTimeout(function(){ // 执行时传入参数 func.apply(context, arg) },wait) } }
此时还有一个问题,最开始函数必需要在事件初次触发后,等待wait以后才会执行,咱们想要用一次当即执行来兜底初次触发:this
function _debounce(func, wait, immediate){ var timer return function(){ var context = this var arg = arguments if(timer) clearTimeout(timer) if(immediate){ /** timer为true,callNow为false期间 * 都表明不是初次触发 * timer初始布尔值为false * 初次执行以后timer在wait时间内一直为true **/ var callNow = !timer // 这一句在执行完以前 timer都为true timer = setTimeout(function(){ timer = null },wait) if(callNow) func.apply(context,arg) }else{ timer = setTimeout(function(){ func.apply(context, arg) },wait) } } } // 中心思想就是有一个标识,用来标记当前是否为初次触发,用以判断是否须要执行,而在执行后wait时间后将标识变为初始状态
**特别版本:防抖函数有可能会形成一直不触发的现象code
function throttle(fn, delay){ /*last为上一次触发回调的时间, timer是定时器*/ var last = 0, timer = null return function () { let context = this let args = arguments let now = +new Date() /*判断上次触发的时间和本次触发的时间差是否小于时间间隔的阈值*/ if (now - last < delay) { /* 若是时间间隔小于咱们设定的时间间隔阈值, * 则为本次触发操做设立一个新的定时器 * 其实这里就是本来正常的防抖逻辑 */ clearTimeout(timer) timer = setTimeout(function () { last = now fn.apply(context, args) }, delay) } else { /* 若是时间间隔超出了咱们设定的时间间隔阈值,那就不等了,不管如何要反馈给用户一次响应*/ last = now fn.apply(context, args) } } }
还能够有一些优化,好比有返回值时等等,暂时到这里 最好能记牢对象
节流:事件持续触发期间,每间隔预设的时间才会执行一次事件
手写+逐步完善(定时器 or 时间戳)
一、时间戳版本rem
function _throttle(func, wait){ var timer = 0 return function(){ var arg = arguments var now = new Date() if(now - timer >= wait){ func.apply(this,arg) timer = now } } } /** 这里懵逼了,理解一下应该是将throttle执行后的结果, * 也就是闭包函数赋值给了container的mousemove * 以后每次mousemove执行的都是闭包函数 **/ container.onmousemove = throttle(xxx,1000)
二、定时器版本
function _throttle(func,wait){ var timer return function(){ var arg = arguments if(!timer){ timer = setTimeout(function(){ timer = null func.apply(this,arg) },wait) } } }
以上两种实现方式,时间戳版本会当即执行,但不会有最后一一次执行;定时器版本不会当即执行,但会有最后一次操做执行兜底
最好是将这两种状况合二为一,更加优秀
三、取长补短版本:
function throttle(func, wait) { var timeout, context, args, result; var previous = 0; var later = function() { previous = +new Date(); timeout = null; func.apply(context, args) }; var throttled = function() { var now = +new Date(); //下次触发 func 剩余的时间 var remaining = wait - (now - previous); context = this; args = arguments; // 若是没有剩余的时间了或者你改了系统时间 if (remaining <= 0 || remaining > wait) { // 对于时间戳计算,要执行的时候若是还有计时器捣乱 要清掉 if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; func.apply(context, args); } else if (!timeout) { // 若是时间戳尚未到执行,定时器到了(没有定时器表明要执行了),那就在剩余时间后执行,兜最后一次 timeout = setTimeout(later, remaining); } }; return throttled; } // 这个难 暂时先理解吧