在实际开发应用中,常常会碰到高频率的事件处理,好比 window 的 scroll, resize 以及 keyup,mousemove 等事件。这些高频率的事件触发会带来一些显著的问题。 若是事件处理函数调用的频率无限制,会加剧浏览器的负担,致使用户体验很是糟糕。javascript
高频率的事件处理函数中,存在着大量的DOM操做,当浏览器的渲染速度跟不上事件触发频率,容易形成页面卡顿,影响用户体验,甚至会形成 CPU 使用率太高致使页面崩溃。 java
高频率的事件处理函数中,每执行一次事件处理函数时,都须要和服务器通讯创建 HTTP 请求,好比页面的搜索功能,那么可能存在短期内发起了数十条 HTTP 请求的状况,容易消耗服务器资源。 浏览器
针对这些问题的解决方案,能够采用函数防抖(debounce)和节流(throttle)的方案解决存在的问题,经过防抖和节流能够将多个事件的触发合并成一个,减小事件触发频率。 同时又不影响实际效果。服务器
我的理解 函数防抖就是法师发技能的时候要读条,技能读条没完再按技能就会从新读条。app
函数防抖(debounce) : 当持续触发事件时,必定时间段内没有再触发事件,事件处理函数才会执行一次,若是设定的时间到来以前,又一次触发了事件,就从新开始延时。以下图,持续触发scroll事件时,并不执行handle函数,当1000毫秒内没有触发scroll事件时,才会延时触发scroll事件。 dom
防抖代码啊啊啊,监视窗口滚动事件函数
<script> function debounce(fn, wait) { var timeout = null; return function() { if(timeout !== null) clearTimeout(timeout); timeout = setTimeout(fn, wait); } } // 处理函数 function handle() { console.log(Math.random()); } // 滚动事件 window.addEventListener('scroll', debounce(handle, 1000)); </script>
咱们能够看到,当持续触发scroll事件时,事件处理函数handle只在中止滚动1000毫秒以后才会调用一次,也就是说在持续触发scroll事件的过程当中,事件处理函数handle一直没有执行。 this
我的理解 函数节流就是fps游戏的射速,就算一直按着鼠标射击,也只会在规定射速内射出子弹。spa
函数节流:当持续触发事件时,保证必定时间段内只调用一次事件处理函数。节流通俗解释就好比咱们水龙头放水,阀门一打开,水哗哗的往下流,秉着勤俭节约的优良传统美德,咱们要把水龙头关小点,最好是如咱们心意按照必定规律在某个时间间隔内一滴一滴的往下滴。以下图,持续触发scroll事件时,并不当即执行handle函数,每隔1000毫秒才会执行一次handle函数。 code
函数节流主要有两种实现方法:时间戳和定时器。接下来分别用两种方法实现throttle~
节流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(Math.random()); } window.addEventListener('scroll', throttle(handle, 1000));
当高频事件触发时,第一次会当即执行(给scroll事件绑定函数与真正触发事件的间隔通常大于delay,若是你非要在网页加载1000毫秒之内就去滚动网页的话,我也没办法o(╥﹏╥)o),然后再怎么频繁地触发事件,也都是每delay时间才执行一次。而当最后一次事件触发完毕后,事件也不会再被执行了(最后一次触发事件与倒数第二次触发事件的间隔小于delay,为何小于呢?由于大于就不叫高频了呀(╹▽╹))。
节流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(Math.random()); } window.addEventListener('scroll', throttle(handle, 1000));
当触发事件的时候,咱们设置一个定时器,再次触发事件的时候,若是定时器存在,就不执行,直到delay时间后,定时器执行执行函数,而且清空定时器,这样就能够设置下个定时器。当第一次触发事件时,不会当即执行函数,而是在delay秒后才执行。然后再怎么频繁触发事件,也都是每delay时间才执行一次。当最后一次中止触发后,因为定时器的delay延迟,可能还会执行一次函数。
节流中用时间戳或定时器都是能够的。更精确地,能够用时间戳+定时器,当第一次触发事件时立刻执行事件处理函数,最后一次触发事件后也还会执行一次事件处理函数。
节流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(Math.random()); } window.addEventListener('scroll', throttle(handle, 1000));
在节流函数内部使用开始时间startTime、当前时间curTime与delay来计算剩余时间remaining,当remaining<=0时表示该执行事件处理函数了(保证了第一次触发事件就能当即执行事件处理函数和每隔delay时间执行一次事件处理函数)。若是还没到时间的话就设定在remaining时间后再触发(保证了最后一次触发事件后还能再执行一次事件处理函数)。固然在remaining这段时间中若是又一次触发事件,那么会取消当前的计时器,并从新计算一个remaining来判断当前状态
函数防抖:将几回操做合并为一此操做进行。原理是维护一个计时器,规定在delay时间后触发函数,可是在delay时间内再次触发的话,就会取消以前的计时器而从新设置。这样一来,只有最后一次操做能被触发。
函数节流:使得必定时间内只触发一次函数。原理是经过判断是否到达必定时间来触发函数。
区别: 函数节流无论事件触发有多频繁,都会保证在规定时间内必定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 好比在页面的无限加载场景下,咱们须要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操做时才去请求数据。这样的场景,就适合用节流技术来实现。
原文:(https://mp.weixin.qq.com/s/Vkshf-nEDwo2ODUJhxgzVA)