防抖和节流
前言
前端的应用场景中,当某事件被触发则执行回调函数,如果不进行防抖和节流,浏览器的一些事件,如:resize,scroll,keydown,keyup,keypress,mousemove等。这些事件触发频率太过频繁,绑定在这些事件上的回调函数会不停的被调用。这样浏览器的目的是为了保证信息的一致性,而对于我们来说就是一种资源的浪费了。
防抖的原理:事件响应函数在一段时间后执行;如果在规定时间内触发,则重新计算执行时间;当在一段时间内没有再次调用该函数,则立即执行该函数。
这个处理是基于DOM操作是十分巨大的开销。所以如果实际的代码中的回调函数只是处理一些js的数据,那么用不用防抖和节流处理是一样的。
正文
封装两个函数分别实现防抖和节流:
debounce:防抖,作用是在让在用户动作停止后延迟x ms再执行回调。
throttle:节流,作用是在用户动作时每隔一定时间(如300ms)执行一次回调。
下面以一个dom元素的onmousemove事件来举例说明:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style type='text/css'> #content-main { width: 100%; height: 360px; background: green; text-align: center; color: aliceblue; line-height: 360px; cursor: pointer; } </style> <script> var i = 0; window.onload = function () { /*防抖测试*/ var doCallBack = debounce(callback, 2000); document.querySelector('#content-main').onmousemove = doCallBack; /*停止延迟事件的回调*/ document.querySelector('#cancel').onclick = function () { doCallBack.cancel(); } /*节流测试 var doCallBack = debounce(callback, 2000); document.querySelector('#content-main').onmousemove = doCallBack; */ } function debounce(func, wait, immediate) { var timeout, result; var debounced = function () { var slf = this; var callback_args = arguments; clearTimeout(timeout); if (immediate) { var callnow = !timeout; timeout = setTimeout(function () { timeout = null; }, wait); if (callnow) result = func.apply(slf, callback_args); } else { timeout = setTimeout(function () { func.apply(slf, callback_args); }, wait); } return result; } debounced.cancel = function () { clearTimeout(timeout); } return debounced; } function throttle(func, wait,options) { var old = 0; var timeout; if(!options) options={}; return function () { var slf = this; var callback_args = arguments; var now = new Date().valueOf(); if (options.leading == false && !old) { old = now; } if (now - old > wait) { if (timeout) { clearTimeout(timeout); timeout = null; } func.apply(slf, callback_args); old = now; } else if (!timeout && options.trailing != false) { timeout = setTimeout(function () { func.apply(slf, callback_args); }, wait); } } } function callback(arguments) { this.innerHTML = 'count:' + (++i); console.log(this); console.log(arguments); } </script> </head> <body> <div id='content-main'>鼠标移动到此区域,触发该区域的onmousemove事件</div> <!--用于防抖测试时,停止延迟事件的回调--> <button id='cancel'>取消</button> </body> </html>