前端开发中会遇到一些频繁的事件触发,好比:window的scroll、resize;mousedown、mousemove,keyup、keydown等等,假如你对本身的代码不作什么的处理,你会发现页面卡顿、触发接口请求频繁等问题,本文将浅析函数节流跟防抖实现,一步一步逐渐揭开函数节流跟防抖的真面目💜javascript
理解防抖跟节流触发原理,根据不一样使用场景合理使用前端
当调用动做过n毫秒后,才会执行该动做,若在这n毫秒内又调用此动做则将从新计算执行时间,不会执行
复制代码
理解原理:
尽管触发事件,可是必定在事件触发 n 秒后才执行,若是在一个事件触发的 n 秒内又触发了这个事件,就以新的事件的时间为准,n秒后才执行,总之,就是要等触发完事件 n 秒内再也不触发事件,才会执行!java
预先设定一个执行周期,当调用动做的时刻大于等于执行周期则执行该动做,而后进入下一个新周期
复制代码
理解原理:
规定时间内,保证执行一次该函数react
根据防抖原理,实现代码以下,以后的示例都将是常规使用:
git
# 箭头log函数 let count = 0 const log = () => { console.log(this) ++count console.log(count) } # 函数表达式 const log = function (evt) { console.log(this) console.log(evt) ++count console.log(count) } 复制代码
const debounce = function (fn, delay){ let timeout = null return function () { if (timeout) { clearTimeout(timeout) } timeout = setTimeout(fn, delay) } } 复制代码
使用频繁click事件为例
常规使用: meContain.onclick = debounce(log, 1000)
react demo: ...onClick={debounce(log.bind(this), 1000)} github
小伙伴们有没有发现此时的防抖函数仍存在缺陷segmentfault
this指向
bash
<div id="mecontain"></div>
event 对象markdown
解决以上问题,来更改咱们的代码app
const debounce = function (fn, delay){ let timeout = null return function () { const context = this; const args = arguments; clearTimeout(timeout) timeout = setTimeout(() => { fn.apply(context, args) }, delay) } } 复制代码
若是但愿马上执行函数一次,不用等到事件中止触发后才执行,而后等到中止触发 n 秒后,再能够从新触发执行
经过添加isImmeDiate来判断是否马上执行
const debounce = function (fn, delay,isImmeDiate= false){ let timeout = null return function () { const context = this; const args = arguments; if(timeout) clearTimeout(timeout) if(isImmeDiate) { # 判断是否已经执行过,不要重复执行 let callNow = !timeout timeout = setTimeout(function(){ timeout = null; }, delay) if(callNow) result = fn.apply(context, args) } else { timeout = setTimeout(() => { fn.apply(context, args) }, delay) } return result } } 复制代码
若是要添加一个取消debounce开关,只须要添加一个cancle函数清除定时器timeout = null
const debounce = function (fn, delay,isImmeDiate= false){ let timeout = null const debounced = function () { const context = this; const args = arguments; if(timeout) clearTimeout(timeout) if(isImmeDiate) { # 判断是否已经执行过,不要重复执行 # setTimeout也是一直在更新的 let callNow = !timeout timeout = setTimeout(function(){ timeout = null; }, delay) if(callNow) result = fn.apply(context, args) } else { timeout = setTimeout(() => { fn.apply(context, args) }, delay) } return result } debounced.prototype.cancle = function() { clearTimeout(timeout) timeout = null } return debounced } 复制代码
到这里咱们实现了一个防抖函数,可是小伙伴们有没有别的想法呢?
根据节流原理,实现代码以下,以后的示例都将是常规使用:
时间戳实现
const throttle = function (fn, delay) { let preTime = 0; return function () { const context = this; const args = arguments; const now = +new Date(); if (now - preTime > delay) { fn.apply(context, args); preTime = now; } } } 复制代码
定时器实现
const throttle = function (fn, delay) { let timeout = null return function () { const context = this; const args = arguments; if (!timeout) { timeout = setTimeout(function(){ timeout = null fn.apply(context, args) }, delay) } } } # 若是须要马上执行,其实变动下执行顺序便可 timeout = setTimeout(function(){ timeout = null //fn.apply(context, args) }, delay) fn.apply(context, args) 复制代码
一样使用频繁click事件为例 常规使用: meContain.onclick = throttle(log, 1000)
小伙伴们有没有发现此时的节流函数存在的特色
const throttle = function (fn, delay) { let timeout = null let preTime = 0; const later = function() { preTime = +new Date() timeout = null fn.apply(context, args); } const throttled = function () { const context = this; const args = arguments; const now = +new Date(); #下次触发fn剩余的时间 const remaining = delay - ( now - preTime) #若是没有剩余的时间了或者系统时间变动 if (remaining <= 0 || remaining > delay) { if(timeout) { clearTimeout(timeout) timeout = null } preTime = now fn.apply(context, args); } else if (!timeout) { timeout = setTimeout(later, remaining) } } throttled.cancel = function() { clearTimeout(timeout); previous = 0; timeout = null; } return throttled } 复制代码
节流,在规定时间内,保证执行一次该函数;防抖,当持续触发事件时,必定时间段内没有再触发事件,事件处理函数才会执行一次,若是设定的时间到来以前,又一次触发了事件,就从新开始延时