防抖(debounce) setTimeout 方法 1 2 3 4 5 6 7 8 9 10 11 var debounce = function (fn, delayTime ) { var timeId; return function ( ) { var context = this , args = arguments ; timeId && clearTimeout (timeout); timeId = setTimeout (function { fn.apply(context, args); }, delayTime) } }
思路解析: 执行 debounce 函数之后会返回一个新的函数,通过闭包的形式,维护一个变量 timeId ,每次执行该函数的时候会结束之前的延迟操作,重新执行 setTimeout 方法,也就实现了上面所说的指定的时间内多次触发同一个事件,会合并执行一次。
温馨提示:
上述代码中arguments只会保存事件回调函数中的参数,譬如:事件对象等,并不会保存 fn、delayTime
使用 apply 改变传入的fn方法中的this指向,指向绑定事件的 DOM 元素。
节流(throttle) 时间戳 1 2 3 4 5 6 7 8 9 10 11 12 var throttle = (fn, delayTime ) => { var _start = Date .now(); return function ( ) { var _now = Date .now(), context = this , args = arguments ; if (_now - _start >= delayTime) { fn.apply(context, args); _start = Date .now(); } }; };
思路解析:我们设置了一个标志变量 flag,当 delayTime 之后执行事件回调,便会把这个变量重置,表示一次回调已经执行结束。
温馨提示:
上述代码中arguments只会保存事件回调函数中的参数,譬如:事件对象等,并不会保存 fn、delayTime
使用 apply 改变传入的fn方法中的this指向,指向绑定事件的 DOM 元素。
定时器 1 2 3 4 5 6 7 8 9 10 11 12 13 var throttle = function (fn, delayTime ) { var flag; return function ( ) { var context = this , args = arguments ; if (!flag) { flag = setTimeout (function ( ) { fn.apply(context, args); flag = false ; }, delayTime); } }; };
定时器 + 时间戳 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var throttle = function (fn, delayTime ) { var flag, _start = Date .now(); return function ( ) { var context = this , args = arguments , _now = Date .now(), remainTime = delayTime - (_now - _start); if (remainTime <= 0 ) { fn.apply(this , args); } else { setTimeout (function ( ) { fn.apply(this , args); }, remainTime); } }; };
requestAnimationFrame 1 2 3 4 5 6 7 8 9 10 11 var throttle = function (fn, delayTime ) {var flag; return function ( ) { if (!flag) { requestAnimationFrame(function ( ) { fn(); flag = false ; }); flag = true ; } }
保证在屏幕刷新的时候(对于大多数的屏幕来说,大约16.67ms),可以执行一次回调函数 fn。使用这种方式也存在一种比较明显的缺点,时间间隔只能跟随系统变化,我们无法修改,但是准确性会比 setTimeout 高一些。
温馨提示:
防抖和节流只是减少了事件回调函数的执行次数,并不会减少事件的触发频率。
防抖和节流并没有从本质上解决性能问题,我们还应该注意优化我们事件回调函数的逻辑功能,避免在回调中执行比较复杂的DOM操作,减少浏览器reflow和repaint。
underscore 源码 debounce 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 _.debounce = function (func, wait, immediate ) { var timeout, result; var later = function (context, args ) { timeout = null ; if (args) result = func.apply(context, args); }; var debounced = restArguments(function (args ) { if (timeout) clearTimeout (timeout); if (immediate) { var callNow = !timeout; timeout = setTimeout (later, wait); if (callNow) result = func.apply(this , args); } else { timeout = _.delay(later, wait, this , args); } return result; }); debounced.cancel = function ( ) { clearTimeout (timeout); timeout = null ; }; return debounced; };
throttle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 _.throttle = function (func, wait, options ) { var timeout, context, args, result; var previous = 0 ; if (!options) options = {}; var later = function ( ) { previous = options.leading === false ? 0 : _.now(); timeout = null ; result = func.apply(context, args); if (!timeout) context = args = null ; }; var throttled = function ( ) { var now = _.now(); if (!previous && options.leading === false ) previous = now; var remaining = wait - (now - previous); context = this ; args = arguments ; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout (timeout); timeout = null ; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null ; } else if (!timeout && options.trailing !== false ) { timeout = setTimeout (later, remaining); } return result; }; throttled.cancel = function ( ) { clearTimeout (timeout); previous = 0 ; timeout = context = args = null ; }; return throttled; };
以上是我对下列视频及文章的归纳和总结。十分钟学会防抖和节流 √ 函数防抖与函数节流