手写防抖节流函数
Posted 饮尽杯中月
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手写防抖节流函数相关的知识,希望对你有一定的参考价值。
防抖
- 每次都检查timeout是否有内容,如果有就clearTimeout,然后生成一个新的timeout,这个新的timeout的回调函数是这个传入的func,等待时间为wait
- 改变debouned函数的作用域到正确(this和arguments指向)
- let context = this; let args = arguments;
- func.apply(context, args);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<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>
<button id="btn">取消防抖</button>
<script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js"></script>
<script>
</script>
<!-- <script src="./debounce.js"></script> -->
<script src="./throttle.js"></script>
</body>
</html>
//防抖:
//事件响应函数(doSomeTing)在一段时间后(300ms)才执行,如果在这段时间内再次调用,则重新计算执行时间
//应用场景:
//1.scroll事件滚动触发
//2.搜索框输入查询
//3.表单验证
//4.按钮提交事件
//5.浏览器窗口缩放,resize事件
function debounce(func, wait, immediate) {
//优化,如果doSomeThing有return值,此时可以用result来接收防抖优化后的doSomeThing的返回值
let timeout, result;
//函数也是对象 debounced对象
let debounced = function() {
//改变执行函数func内部的this指向,从doSomeTing的windows到doSome()的container
let context = this;
//改变event的指向问题,从doSomeTing接收的event为underfind到doSome接收的event为onmousemove事件
let args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
//1.当首次激发事件,timout为null,所以callNow为true可以立即执行
//2.当首次激发事件,但是没有到达wait时间,再次激发事件,此时timeout有值,
//所以callNow为false,不能立即执行,完成防抖操作
//3.当长时间没有再次激发事件,就将timeout=null,方便下次可以再次立即执行
let callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait);
//立即执行
if (callNow) result = func.apply(context, args);
} else {
timeout = setTimeout(function() {
func.apply(context, args);
}, wait);
}
return result;
}
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
}
return debounced;
}
let count = 0;
let container = document.querySelector('#container');
let btn = document.querySelector('#btn');
function doSomeTing(e) {
//可能会做回调或者ajax请求
container.innerHTML = count++;
//e:underfind
console.log(e);
return '想要的东西'
}
let doSome = debounce(doSomeTing, 300, true);
btn.onclick = function() {
//取消防抖操作
doSome.cancel();
}
container.onmousemove = doSome;
节流
//节流
//原理:如果你持续触发事件,每隔一段时间,只执行一次事件
//应用场景:
//1.DOM元素的拖拽功能实现
//2.射击游戏类
//3.计算鼠标移动的距离
//4.监听scroll滚动事件
function throttle(func, wait, options) {
let context, args, timeout;
let old = 0;
//如果用户不传options
if (!options) options = {};
let later = function() {
old = new Date().valueOf();
timeout = null;
func.apply(context, args);
}
return function() {
context = this;
agrs = arguments;
//每一次被运行时,都会获得当下的时刻
let now = new Date().valueOf();
if (options.leading === false && !old) {
//更新old
old = now;
}
if (now - old > wait) {
//第一次执行 ,最后一次不执行
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
func.apply(context, args);
old = now;
}// 当触发事件时候,会设置定时器,此时定时器timeout一直存在,无法进入该else if,
//如果再次触发,且wait延迟达到,此时将timeout=null,将再次进入else if
else if (!timeout && options.trailing !== false) {
//第一次不执行,最后一次还会执行
timeout = setTimeout(later, wait);
}
}
}
let count = 0;
let container = document.querySelector('#container');
let btn = document.querySelector('#btn');
function doSomeTing(e) {
//可能会做回调或者ajax请求
container.innerHTML = count++;
//e:underfind
console.log(e);
return '想要的东西'
}
let doSome = throttle(doSomeTing, 5000, {
leading: true,
trailing: true
});
container.onmousemove = doSome;
另一种节流方案
throttle是一个闭包,其返回的函数是这个闭包的入口,而pre是这个闭包隐藏的局部变量,是一个私有变量,而返回函数的执行过程中,会改变pre的值,而当第一次onmousemove 完成后,第二次再次执行onmousemove 事件,想要pre回到最开始状态(pre = null),此时下一次又可以按原始状态进行了。
else里面实现:相当于执行的是最后一次,功能就是将定时器清空,然后过一个interval后将pre回到最开始状态(pre = null),其中还可以实现最后一次计数func.apply(context, args),或者取消最后一次计数,不执行func.apply(context, args),这里按实际需求来编写代码。
以上是关于手写防抖节流函数的主要内容,如果未能解决你的问题,请参考以下文章