官方解释:当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
节流实现思路: 实现节流函数, 我们使用定时器是不方便管理的, 实现节流函数我们采用另一个思路
我们获取一个当前时间nowTime, 我们使用new Date().gettime()方法获取, 在设定一个开始时间startTime, 等待时间waitTime
waitTime = interval - (nowTime - startTime), 当前的时间减去开始的时间得到结果, 再使用间隔时间减去这个结果, 就可以得到等待时间
得到等待时间我们在进行判断, 如果等待时间小于等于0, 那么就可以执行回调函数
开始时间startTime我们初始值为0就好, 当第一次执行时, nowTime获取的时间戳是一个非常大的值, 得到的结果waitTime是负值, 所以第一次执行节流函数, 一定会立即执行, 这也符合我们要封装的效果
基础节流函数(时间差的方式):
/**
* 节流函数
* @param {function} func 需要节流的目标函数
* @param {number} interval 执行目标函数的间隔,即多久执行一次目标函数ms
* 注意:如果初始值为当前时间,则最后一次如果没有达到间隔时间则不会执行
* */
function throttle(func, interval) {
// 记录上次执行的时间。如果初始值设置成零,则会首次触发就会执行目标函数,相当于立即执行。
// let preDoTime = 0;
let preDoTime = new Date().getTime();
// 返回函数, 使用闭包使preDoTime变量存在于内存中。
return function (...args) {
// 记录调用函数的this指向。
let _this = this;
//获取到当前日期
let currentTime = new Date().getTime();
if (interval <= currentTime - preDoTime) {// 如果当前时间减去上次执行时间 大于等于设置的时间间隔, 就可以执行目标函数
func.apply(_this, args)
preDoTime = currentTime;
}
}
}
// 测试程序 start ----
const testFun = function (data) {
console.log(`console.log---${this.name} --- ${data}`);
}
const resultFun = throttle(testFun, 1500);
for (let i = 0; i <= 5; i++) {
setTimeout(function () {
this.name = 'setTimeoutFunc1'
resultFun(`test${i}`);
}, (i + 1) * 1000) // 6秒内, 每秒执行一次resultFun函数
}
// 距离上次触发间隔0.5秒
setTimeout(function () {
this.name = 'setTimeoutFunc2'
resultFun(`test7`);
}, 8000)
// 测试程序 end ----
初始值为当前时间测试结果:
初始值为零测试结果:
节流函数(定时器实现):
/**
* 节流函数
* @param {function} func 需要节流的目标函数
* @param {number} interval 执行目标函数的间隔,即多久执行一次目标函数ms
* 特点: 首次立即执行
* */
function throttle(func, interval) {
let timer = null;
return function(...args) {
let _this = this;
if (!timer) {
timer = setTimeout(function() {
func.apply(_this, args);
timer = null;
}, interval)
}
}
}
// 测试程序 start ----
const testFun = function (data) {
console.log(`console.log---${this.name} --- ${data}`);
}
const resultFun = throttle(testFun, 1500);
for (let i = 0; i <= 5; i++) {
setTimeout(function () {
this.name = 'setTimeoutFunc1'
resultFun(`test${i}`);
}, (i + 1) * 1000) // 6秒内, 每秒执行一次resultFun函数
}
// 距离上次触发间隔0.5秒
setTimeout(function () {
this.name = 'setTimeoutFunc2'
resultFun(`test7`);
}, 8000)
执行结果:
节流函数(时间差+定时器 兼顾首次和最后一次)
/**
* 节流函数
* @param {function} func 需要节流的目标函数
* @param {number} interval 执行目标函数的间隔,即多久执行一次目标函数ms
* 特点: 结合时间差和定时器,做到立即执行的同时,兼顾执行最后一次触发
* */
function throttle(func, interval) {
let timer = null;
let preDoTime = 0;
return function(...args) {
// 保存this指向
let _this = this;
// 获取当前日期
let currentTime = +new Date();
// 计算剩余时间,大于零表示未到执行时间,小于零或者定于零则表示到了执行的时间,可以执行
let remaining = interval - (currentTime - preDoTime);
if (remaining <= 0) {
func.apply(_this, args);
preDoTime = currentTime;
// 首次会进入上一个if,首次的时候是没有timer的,所以需要判空
if(timer) {
// 如果有定时器存在,就将定时器取消。
clearTimeout(timer);
timer = null;
}
} else if(!timer) {
timer = setTimeout(function () {
// 如果是最后一次则没有执行取消定时器的操作,就会执行当前定时器。
preDoTime = +new Date(); // 因为是定时器,所以执行日期要重新获取。
timer = null;
func.apply(_this, args);
}, interval)
}
}
}
// 测试程序 start ----
const testFun = function (data) {
console.log(`console.log---${this.name} --- ${data}`);
}
const resultFun = throttle(testFun, 1500);
for (let i = 0; i <= 5; i++) {
setTimeout(function () {
this.name = 'setTimeoutFunc1'
resultFun(`test${i}`);
}, (i + 1) * 1000) // 6秒内, 每秒执行一次resultFun函数
}
// 距离上次触发间隔0.5秒
setTimeout(function () {
this.name = 'setTimeoutFunc2'
resultFun(`test7`);
}, 8000)
// 测试程序 end ----
测试结果: