防抖
防抖的定义
防抖在事件被触发n秒后在执行回调,如果在这n秒内又被触发,则重新计时。 大白话可以这样理解:你尽管触发事件,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要等你触发完事件 n 秒内不再触发事件,我才执行,真是任性呐!
来个使用场景
<!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>
var count = 1;
var container = document.getElementById('container');
function getUserAction() {
container.innerHTML = count++;
};
container.onmousemove = getUserAction;
</script>
</body>
</html>
每在上图中黑色区域移动鼠标一次,中间的数字+1。
因为这个例子很简单,所以浏览器完全反应的过来,可是如果是复杂的回调函数或是 ajax 请求呢?假设 1 秒触发了 60 次,每个回调就必须在 1000 / 60 = 16.67ms 内完成,否则就会有卡顿出现。
为了解决这个问题,我们可以通过防抖。
防抖最初版
function debounce(fn,deplay) {
let timer = null
return function () {
if (timer) { clearTimeout(timer) }
timer = setTimeOut(func,deplay)
}
}
如果我们要使用它,以最一开始的例子为例:
container.onmousemove = debounce(getUserAction, 1000);
现在随你怎么移动,反正你移动完 1000ms 内不再触发,我才执行事件。
存在的问题:
防抖第二版
1.this指向不对 原本this指向的是container元素,现在加上debounce后,this指向就变为window,这可不行,来解决一下这个问题吧。
// 第二版
function debounce(func, wait) {
var timeout;
return function () {
var context = this;
clearTimeout(timeout)
timeout = setTimeout(function(){
func.apply(context)
}, wait);
}
}
第2个问题: JavaScript 在事件处理函数中会提供事件对象 event,我们修改下 getUserAction 函数:
function getUserAction(e) {
console.log(e);
container.innerHTML = count++;
};
防抖第三版
如果我们不使用 debouce 函数,这里会打印 MouseEvent 对象。 但是在我们实现的 debounce 函数中,却只会打印 undefined! 所以我们再修改一下代码:
// 第三版
function debounce(func, wait) {
var timeout;
var args =a rguments
return function () {
var context = this;
clearTimeout(timeout)
timeout = setTimeout(function(){
func.apply(context, args)
}, wait);
}
}
防抖最终版
最后用ES6来改写一下吧。
// 第四版
function debounce(fn, apply) {
let timer;
return function (...args) {
if (timer) clearTimeout(timer);
let context = this;
timer = setTimeout(function () {
fn.apply(context, args);
});
};
}
节流
节流的定义
如果你持续触发事件,每隔一段时间,只执行一次事件。d
节流最初版
使用时间戳,当触发事件的时候,我们取出当前的时间戳,然后减去之前的时间戳(最一开始值设为 0 ),如果大于设置的时间周期,就执行函数,然后更新时间戳为当前的时间戳,如果小于,就不执行。
// 第一版
function throttle(func, wait) {
var context, args;
var previous = 0;
return function() {
var now = +new Date();
context = this;
args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
节流最终版
ES6改写一下
function throttle(fn, deplay) {
let previous = 0;
return function (...args) {
let now = Date.now();
let context = this;
if (now - previous > deplay) {
fn.apply(context, args);
previous = now;
}
};
}