手写防抖函数和节流函数
防抖函数
function test() { console.log(1) } window.addEventListener(mousemove, debounce(test, 1000)); function debounce(func, wait) { if (typeof func !== function) { throw new TypeError(need a function); } var timer, wait = +wait || 0; return function () { let context = this let args = arguments timer ? clearTimeout(timer) : func.apply(context, args); timer = setTimeout(function () { func.apply(context, args) }, wait); } }
解析一下这段代码:debounce 函数会返回一个匿名函数,产生闭包,目的是为了能够访问到 debounce 函数中的 timer 变量,debounce 函数接收两个参数,第一个参数是需要防抖的函数,第二个参数是防抖的时间,这里监听了鼠标移动 mousemove 事件,当鼠标移动时,第一次触发 debounce 函数时,由于 timer 还没有被赋值,将会直接执行参数传进来的函数,这里 context 和 args 是为了保持原来的执行上下文 this 和 参数,用 apply 改变 this指向实现的,然后设置一个定时器,1秒后再触发需要防抖的函数,并把这个定时器赋值给 timer,如果在一秒之内再次鼠标移动触发 debounce 函数,此时 debounce函数中的 timer 在上一次触发时已经被定时器赋值了,timer 为真,就会执行 clearTimeout(timer) 把上一次设下的定时器给清掉,并设下一个新的定时器,也是1秒之后再触发需要防抖的函数,再复制给 timer,如果这一秒内再一次触发 debounce 函数,还会重复上述的操作,不管多频繁触发,只要下一次触发间隔小于设定的时间,都会把上一次设定的定时器清除掉,也就是最后只会执行一次,实现了防抖的功能,从而提高了性能。
节流函数
function throttle(func, wait) { if (typeof func !== function) { throw new TypeError(need a function); } var timer, wait = +wait || 0; return function () { var context = this; var args = arguments; if (!timer) { func.apply(context, args) timer = setTimeout(function () { timer = null; }, wait) } } }
解析一下这段代码:throttle 函数和上面的防抖函数很像,也是通过返回一个匿名函数形成闭包来保存 timer 变量,当 throttle 被第一次触发时,timer 还没有被赋值,于是会立即执行需要节流的函数,并设下一个定时器,在设定的时间后将 timer 赋值为 null,如果在这个设定的时间之内再次出发 throttle 函数,由于 timer 已经被赋值,!timer 就为假,就不会执行节流函数,等到过了设定的时间,定时器触发,将刚刚被赋值的 timer 设为 null,然后下一次触发 throttle 函数,才会执行节流函数,并再设下一个定时器,这样不管多频繁,只会在规定的时间内执行一次,实现了节流功能,提高了性能。