实际工做中,经过监听某些事件,如scroll
事件检测滚动位置,根据滚动位置显示返回顶部按钮;如resize
事件,对某些自适应页面调整DOM
的渲染;如keyup
事件,监听文字输入并调用接口进行模糊匹配等等,这些事件处理函数调用的频率若是过高,会加剧浏览器的负担,减弱性能,形成用户体验很差。此时须要采用debounce
(防抖)和throttle
(节流)的方式来减小调用频率,同时不影响原来效果。javascript
当持续触发事件时,一段时间段内没有再触发事件,事件处理函数才会执行一次,若是设定的时间到来以前就触发了事件,延时从新开始。
函数防抖的应用场景,最多见的就是用户注册时候的手机号码验证和邮箱验证了。只有等用户输入完毕后,前端才须要检查格式是否正确,若是不正确,再弹出提示语。前端
上图中,持续触发scroll
事件时,并不执行handle
函数,当1000毫秒内没有触发scroll
事件时,才会延时触发scroll
事件;
上面原理:对处理函数进行延时操做,若设定的延时到来以前,再次触发事件,则清除上一次的延时操做定时器,从新定时。
代码以下:java
// 函数防抖 var timer = false; document.getElementById("debounce").onscroll = function(){ clearTimeout(timer); // 清除未执行的代码,重置回初始化状态 timer = setTimeout(function(){ console.log("函数防抖"); }, 1000); };
防抖函数的封装使用浏览器
/** * 防抖函数 * @param method 事件触发的操做 * @param delay 多少毫秒内连续触发事件,不会执行 * @returns {Function} */ function debounce(method,delay) { let timer = null; return function () { let self = this, args = arguments; timer && clearTimeout(timer); timer = setTimeout(function () { method.apply(self,args); },delay); } } window.onscroll = debounce(function () { let scrollTop = document.body.scrollTop || document.documentElement.scrollTop; console.log('滚动条位置:' + scrollTop); },1000)
另外一种写法app
// 防抖 function debounce(fn, wait) { var timeout = null; return function() { if(timeout !== null) clearTimeout(timeout); timeout = setTimeout(fn, wait); } } // 处理函数 function handle() { console.log("函数防抖"); } // 滚动事件 window.addEventListener('scroll', debounce(handle, 1000));
当持续触发事件时,保证必定时间段内只调用一次事件处理函数。
函数节流应用的实际场景,多数在监听页面元素滚动事件的时候会用到。函数
上图中,持续触发scroll
事件时,并不当即执行handle
函数,每隔1000毫秒才会执行一次handle
函数;
函数节流的要点是,声明一个变量当标志位,记录当前代码是否在执行。若是空闲,则能够正常触发方法执行。
代码以下:性能
// 函数节流 定时器 var canRun = true; document.getElementById("throttle").onscroll = function(){ if(!canRun){ // 判断是否已空闲,若是在执行中,则直接return return; } canRun = false; setTimeout(function(){ console.log("函数节流"); canRun = true; }, 300); };
节流函数的封装使用this
//节流throttle代码(时间戳) var throttle = function(func, delay) { var prev = Date.now(); return function() { var context = this; var args = arguments; var now = Date.now(); if (now - prev >= delay) { func.apply(context, args); prev = Date.now(); } } } function handle() { console.log("函数节流"); } window.addEventListener('scroll', throttle(handle, 1000));
//节流throttle代码(定时器) var throttle = function(func, delay) { var timer = null; return function() { var context = this; var args = arguments; if (!timer) { timer = setTimeout(function() { func.apply(context, args); timer = null; }, delay); } } } function handle() { console.log("函数节流"); } window.addEventListener('scroll', throttle(handle, 1000));
// 节流throttle代码(时间戳+定时器): var throttle = function(func, delay) { var timer = null; var startTime = Date.now(); return function() { var curTime = Date.now(); var remaining = delay - (curTime - startTime); var context = this; var args = arguments; clearTimeout(timer); if (remaining <= 0) { func.apply(context, args); startTime = Date.now(); } else { timer = setTimeout(func, remaining); } } } function handle() { console.log("函数节流"); } window.addEventListener('scroll', throttle(handle, 1000));
用时间戳+定时器,当第一次触发事件时立刻执行事件处理函数,最后一次触发事件后也还会执行一次事件处理函数spa