原理
规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
实现
时间戳实现
注意:第一次事件会被触发,最后一次不会触发函数
function throttle(func, wait) {let _this, args;// 之前的时间戳let old = 0;return function () {// 保存this_this = this;//保存argumentsargs = arguments;// 获取当前时间戳let now = new Date().valueOf();if (now - old > wait) {// 立即执行func.apply(_this, args);lod = now;}}}
注意和防抖函数进行区分
定时器实现
第一次不会被触发,最后一次会触发
function throttle(func, wait) {let _this, args, timeout;return function () {_this = this;args = arguments;if (!timeout) {timeout = setTimeout(() => {timeout = null;func.apply(_this, args);}, wait)}}}
节流进阶
原生的节流函数有第三个参数,用于控制是否首次事件是否被立即调用,以及最后一次事件是否被调用
container.onmousemove = _.throttle(doThrottle, 1000, {leading: false, //是否立即执行trailing: true //最后一次是否执行//两者不能同为false,否则就会产生bug,会导致触发事件立即执行一次后就失效了!})
实现
function throttle(func, wait, options) {let _this, args, timeout;let old = 0; //时间戳//如果没有options就将其设置为空对象if (!options) options = {};let later = function () {old = new Date().valueOf();timeout = null;func.apply(_this, args);}return function () {_this = this;args = arguments;let now = new Date().valueOf();if (options.leading === false && !old) {old = now;}if (now - old > wait) {// 第一次直接执行if (timeout) {clearTimeout(timeout);timeout = null;}func.apply(_this, args);old = now;} else if (!timeout && options.trailing !== false) {//最后一次也被执行timeout = setTimeout(later, wait)}}}
测试:
function doThrottle(e) {container.innerHTML = count++}container.onmousemove = throttle(doThrottle, 1000,{leading:false,trailing:true});
应用场景
- DOM元素的拖拽功能的实现
- 射击游戏中的每隔一段时间射击
- 计算鼠标移动的距离
- 监听scroll滚动事件
- 搜索联想
- 高频点击提交,表单重复提交
防抖节流之间的异同比较
- 相同:
- 都可以通过setTimeout实现
- 目的都是:降低回调执行频率,节省计算资源
- 不同:
- 函数防抖: 在一段连续操作结束后,处理回调,利用clearTimeout和setTimeout实现.
- 函数节流:在一段连续操作中,每一段时间只执行一次,频率较高的时间中使用来提高性能.
- 函数防抖关注一定时间连续触发的事件只在最后执行,而函数节流侧重于一段时间内只执行一次!
