- 在浏览器中某些计算和处理要比其余的昂贵不少。例如
DOM
操做比起非DOM
交互须要更多的内存和CPU占用时间。连续尝试进行过多的DOM
操做可能会致使浏览器挂起,甚至崩溃;- 例如当调整浏览器大小的时候,
resize
事件会连续触发;若是在resize
事件处理程序内部尝试进行DOM
操做,其高频率的更改可能会让浏览器崩溃;- 为了绕开上面的问题,须要对该类函数进行节流;
防抖(
debounce
)和节流(throttle
)都是用来控制某个函数在必定时间内执行多少次的技巧,二者类似而又不一样。 背后的基本思想是某些代码不能够在没有间断的状况下连续重复执行。浏览器
debounce
)若是一个事件被频繁触发屡次,而且触发的时间间隔太短,则防抖函数可使得对应的事件处理函数只执行最后触发的一次。 函数防抖能够把多个顺序的调用合并成一次。bash
throttle
)若是一个事件被频繁触发屡次,节流函数能够按照固定频率去执行对应的事件处理方法。 函数节流保证一个事件必定时间内只执行一次。闭包
类型 | 场景 |
---|---|
函数防抖 | 1. 手机号、邮箱输入检测 2. 搜索框搜索输入(只需最后一次输入完后,再放松Ajax请求) 3. 窗口大小 resize (只需窗口调整完成后,计算窗口大小,防止重复渲染)4.滚动事件 scroll (只需执行触发的最后一次滚动事件的处理程序)5. 文本输入的验证(连续输入文字后发送 AJAX 请求进行验证,(中止输入后)验证一次就好 |
函数节流 | 1. DOM 元素的拖拽功能实现(mousemove )2. 射击游戏的 mousedown /keydown 事件(单位时间只能发射一颗子弹)3. 计算鼠标移动的距离( mousemove )4. 搜索联想( keyup )5. 滚动事件 scroll ,(只要页面滚动就会间隔一段时间判断一次) |
function debounce(fn, delay, scope) {
let timer = null;
// 返回函数对debounce做用域造成闭包
return function () {
// setTimeout()中用到函数环境老是window,故须要当前环境的副本;
let context = scope || this, args = arguments;
// 若是事件被触发,清除timer并从新开始计时
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
}
}
复制代码
- 代码解读
- 第一次调用函数,建立一个定时器,在指定的时间间隔以后运行代码;
- 当第二次调用该函数时,它会清除前一次的定时器并设置另外一个;
- 若是前一个定时器已经执行过了,这个操做就没有任何意义;
- 然而,若是前一个定时器还没有执行,其实就是将其替换为一个新的定时器;
- 目的是只有在执行函数的请求中止了
delay
时间以后才执行。
function throttle(fn, threshold, scope) {
let timer;
let prev = Date.now();
return function () {
let context = scope || this, args = arguments;
let now = Date.now();
if (now - prev > threshold) {
prev = now;
fn.apply(context, args);
}
}
}
复制代码
function throttle2(fn, threshold, scope) {
let timer;
return function () {
let context = scope || this, args = arguments;
if (!timer) {
timer = setTimeout(function () {
fn.apply(context, args);
timer = null;
}, threshold)
}
}
}
复制代码
scroll
事件).wrap {
width: 200px;
height: 330px;
margin: 50px;
margin-top: 200px;
position: relative;
float: left;
background-color: yellow;
}
.header{
width: 100%;
height: 30px;
background-color: #a8d4f4;
text-align: center;
line-height: 30px;
}
.container {
background-color: pink;
box-sizing: content-box;
width: 200px;
height: 300px;
overflow: scroll;
position: relative;
}
.content {
width: 140px;
height: 800px;
margin: auto;
background-color: #14ffb2;
}
复制代码
<div class="wrap">
<div class="header">滚动事件:普通</div>
<div class="container">
<div class="content"></div>
</div>
</div>
<div class="wrap">
<div class="header">滚动事件:<strong>加了函数防抖</strong></div>
<div class="container">
<div class="content"></div>
</div>
</div>
<div class="wrap">
<div class="header">滚动事件:<strong>加了函数节流</strong></div>
<div class="container">
<div class="content"></div>
</div>
</div>
复制代码
let els = document.getElementsByClassName('container');
let count1 = 0,count2 = 0,count3 = 0;
const THRESHOLD = 200;
els[0].addEventListener('scroll', function handle() {
console.log('普通滚动事件!count1=', ++count1);
});
els[1].addEventListener('scroll', debounce(function handle() {
console.log('执行滚动事件!(函数防抖) count2=', ++count2);
}, THRESHOLD));
els[2].addEventListener('scroll', throttle(function handle() {
console.log(Date.now(),', 执行滚动事件!(函数节流) count3=', ++count3);
}, THRESHOLD));
复制代码
// 函数防抖
function debounce(fn, delay, scope) {
let timer = null;
let count = 1;
return function () {
let context = scope || this,
args = arguments;
clearTimeout(timer);
console.log(Date.now(), ", 触发第", count++, "次滚动事件!");
timer = setTimeout(function () {
fn.apply(context, args);
console.log(Date.now(), ", 可见只有当高频事件中止,最后一次事件触发的超时调用才能在delay时间后执行!");
}, delay);
}
}
复制代码
// 函数节流
function throttle(fn, threshold, scope) {
let timer;
let prev = Date.now();
return function () {
let context = scope || this, args = arguments;
let now = Date.now();
if (now - prev > threshold) {
prev = now;
fn.apply(context, args);
}
}
}
复制代码
debounce
和throttle
均是经过减小高频触发事件的实际事件处理程序的执行来提升事件处理函数运行性能的手段,并无实质上减小事件的触发次数。debounce
能够把多个顺序的调用合并成一次。throttle
保证一个事件必定时间内只执行一次。