在前端开发中会遇到一些频繁的事件触发,比如:
window 的 resize、scroll
mousedown、mousemove
keyup、keydown
……
为此,我们举个示例代码来了解事件如何频繁的触发:
<!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="IE=edge, chrome=1"> <title>debounce</title> <style> #container { width: 100%; height: 200px; line-height: 200px; text-align: center; color: #fff; background-color: #444; font-size: 30px; } </style> </head> <body> <div id="container"></div> <script src="./防抖函数.js"></script> </body> </html>
// 防抖函数.js var count = 1; var container = document.getElementById('container'); function getUserAction(e) { console.log(this) // this 应该指向div console.log(e) // e 不应该打印undefined container.innerHTML = count++; }; // container.onmousemove = getUserAction; // this 指向div // container.onmousemove = debounce2(getUserAction, 500); container.onmousemove = debounce3(getUserAction, 500,true);
function debounce1(func, wait) { let timeout; return function () { // 清除上一次的定时器,如果前后两次时间间隔小于等待时间函数就无法执行 // 只有前后两次时间间隔大于等待时间才有可能触发函数事件 clearTimeout(timeout); timeout = setTimeout(func, wait); } }
解决getUserAction
函数 this
指向问题
function debounce2(func, wait) { let timeout; return function () { let that = this; // 清除上一次的定时器,如果前后两次时间间隔小于等待时间函数就无法执行 // 只有前后两次时间间隔大于等待时间才有可能触发函数事件 clearTimeout(timeout); // 使用apply 函数改变func 函数this 指向 timeout = setTimeout(function () { func.apply(that) }, wait); } }
解决getUserAction
函数event
对象 打印undefined
问题
function debounce2(func, wait) { let timeout; return function () { // this 指向调用该函数的对象 let that = this; // 当前函数 arguments 中存储了MouseEvent 事件 let args = arguments; // 清除上一次的定时器,如果前后两次时间间隔小于等待时间函数就无法执行 // 只有前后两次时间间隔大于等待时间才有可能触发函数事件 clearTimeout(timeout); // 使用apply 函数改变func 函数this 指向 timeout = setTimeout(function () { func.apply(that, args) }, wait); } }
我不希望非要等到事件停止触发后才执行,我希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行。
immediate
参数判断是否是立刻执行
function debounce3(func, wait, immediate) { let timeout; return function () { let that = this; let args = arguments; if (timeout) clearTimeout(timeout); // true 立即执行 if (immediate) { // 如果已经执行过,不再执行 let callNow = !timeout; // 等待N秒后 赋值null。赋值为null后如果下次事件触发事件小于等待时间,函数依旧不会被执行 timeout = setTimeout(function () { timeout = null; }, wait) // 如果timeout 为undefined或null时,callNow 为true,立即执行函数 if (callNow) { func.apply(that, args) } } else { timeout = setTimeout(func.apply(that, args), wait) } } }