1. 防抖
我们在平时开发的时候,会有很多的场景会频繁触发事件,比如说搜索框实时发送请求。
oumousemove、resize、onscroll等等,有些时候我们并不想频繁触发事件,咋办呢?这个时候我们就可以用到函数的防抖和节流了。
1. 直接使用underscore.js来实现这些功能。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#container {
width: 100%;
height:200px;
line-height: 200px;
text-align: center;
color:#fff;
background-color: #444;
background-size: 30px;
}
</style>
</head>
<body>
<div id="container"></div>
<script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js"></script>
<script>
// 演示事件是如何频繁发生的
let count = 0;
let container = document.querySelector('#container')
function doSomething() {
// 可能会做回调或者ajax请求
container.innerHTML = count++;
}
// 防抖函数
// container.onmousemove = _.debounce(doSomething, 1000);
container.onmousemove = _.debounce(doSomething, 1000, true);
// 防抖原理
// 事件响应函数在一段时间之后才执行,如果在这段时间内再次调用,则重新计算执行时间
// 当预定的时间内没有再次调用该函数,则执行doSomething函数
</script>
</body>
</html>
2. 实现简单的防抖函数
function debounce(func,wait) {
let timeout;
return function() {
clearTimeout(timeout);
timeout = setTimeout(func, wait); // 定时器的id 为 timeout
}
}
let count = 0;
// 演示事件是如何频繁发生的
let container = document.querySelector('#container');
function doSomething() {
// 可能会做回调或者ajax请求
container.innerHTML = count++;
}
// 高阶函数 防抖
container.onmousemove = debounce(doSomething, 300)
就是每次触发事件的将上次的定时器任务取消,这样就不会在短时间事件被频繁触发。
4. 防抖的初步实现
// 防抖的函数
function debounce(func, wait, immediate) {
let timeout;
return function() {
// console.log(arguments);
// 改变执行函数内部 this 的指向
// console.log(this); // container容器对象
let context = this;
// 获取执行函数的实参
let args = arguments;
clearTimeout(timeout); // 每次触发事件就先取消定时器
if(immediate) {
let callNow = !timeout; // 通过对timeout变量取反 判断是否立即执行(一开始 timeout 为 undefined,则布尔值为false)
timeout = setTimeout(() => {
timeout = null; // 通过对timeout变量置空来设置下一次的立即执行
}, wait);
// 立即执行,并改变this的指向
if (callNow) func.apply(context, args)
} else {
// 不会立即执行
timeout = setTimeout(function() {
func.apply(context, args);
}, wait);
}
}
}
// 演示事件是如何频繁发生的
let count = 0;
let container = document.querySelector('#container')
function doSomething(e) {
// e 是事件对象
console.log(e)
// console.log(this) // container容器对象
// 可能会做回调或者ajax请求
container.innerHTML = count++;
}
// 防抖函数
// container.onmousemove = debounce(doSomething, 1000, true); // true 立即执行(只要鼠标进去就立即执行)
container.onmousemove = debounce(doSomething, 1000);
5. 防抖函数中的返回值和取消操作实现
function debounce(func, wait, immediate) {
var timeout, result;
let decounced = function() {
var context = this;
var args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(function () {
timeout = null;
}, wait);
// 立即执行
if (callNow) result = func.apply(context, args)
} else {
// 不会立即执行
timeout = setTimeout(function () {
func.apply(context, args);
}, wait);
}
return result;
}
decounced.cancel = function () {
clearTimeout(timeout);
timeout = null;
}
return decounced;
}
let count = 0;
let container = document.querySelector('#container')
let btn = document.querySelector('#btn')
function doSomething(e) {
// e 是事件对象(要注意event的指向问题)
console.log(e)
// 改变执行函数内部this的指向问题
// console.log(this) // container容器对象
// 可能会做回调或者ajax请求
container.innerHTML = count++;
return '想要的结果';
}
let doSome = debounce(doSomething, 10000)
btn.onclick = function () {
doSome.cancel()
}
// 防抖函数
// container.onmousemove = debounce(doSomething, 1000, true); // true 立即执行(只要鼠标进去就立即执行)
container.onmousemove = doSome;
总结:
- 事件响应函数(doSomething) 在一段时间后(300ms)才执行,如果在这段时间内再次调用,则重新计算执行时间;当预定的时间内
- 应用场景:
- scroll 事件滚动触发
- 搜索框输入查询
- 表单验证
- 按钮提交事件
- resize (浏览器的窗口缩放)
2. 节流
原理:如果持续的去触发事件,每隔一段时间只会执行一次事件