实现
function throttle(func, wait, immediate) {
// 多个参数及传递和不传递的默认处理
if (typeof func !== 'function') throw new TypeError('func must be a function');
if (typeof wait === 'undefined') wait = 500;
if (typeof wait === 'boolean') {
immediate = wait
wait = 500
}
if (typeof immediate !== 'boolean') immediate = false;
// 是否需要立即执行首次
// 是:可不必定时器,去触发延迟执行
if (immediate) {
// 方式一:无需定时器函数
previous = 0; // 记录上一次操作的时间
return function proxy(...params) {
let self = this,
now = +new Date();// 当前这次触发操作的时间
// 判断是否需要执行
if (
previous === 0 // 第一次执行(上次执行时间为默认值0)
|| now >= previous + wait // 或超过限制时间节点(当前时间>=上次执行时间+延迟时间)时
) {
previous = now
func.call(self, ...params)
}
}
}
// 否
else {
let timer = null
return function proxy(...params) {
// 是否设置过定时器函数
// 是:退出
if (timer) return;
// 否:没有设置过,则立即执行,且设置定时器标识截断
timer = setTimeout(function () {
func.call(self, ...params)
timer = null // 把定时器标识重置为null
}, wait)
}
}
}
function handle() {
console.log('ok')
}
document.body.style.height = '5000px'
window.onscroll = throttle(handle, true)
分析过程
function throttle(func, wait, immediate) {
if (typeof func !== 'function') throw new TypeError('func must be a function');
if (typeof wait === 'undefined') wait = 500;
if (typeof wait === 'boolean') {
immediate = wait
wait = 500
}
if (typeof immediate !== 'boolean') immediate = false;
// 是否需要立即执行首次
// 是:可不必定时器,去触发延迟执行
if (immediate) {
// 方式一:无需定时器函数
previous = 0; // 记录上一次操作的时间
return function proxy(...params) {
let self = this,
now = +new Date();// 当前这次触发操作的时间
// 判断是否需要执行
if (
previous === 0 // 第一次执行(上次执行时间为默认值0)
|| now >= previous + wait // 或超过限制时间节点(当前时间>=上次执行时间+延迟时间)时
) {
previous = now
func.call(self, ...params)
}
}
// // 方式二:定时器函数-lodash
// let timer = null,
// previous = 0; // 记录上一次操作的时间
// return function proxy(...params) {
// let self = this,
// now = new Date(),// 当前这次触发操作的时间
// remaining = wait - (now - previous)
// if (remaining <= 0) {
// // 两次间隔时间,都超过wait了,直接执行即可
// clearTimeout(timer); // 兼容临界点
// timer = null;
// previous = now
// func.call(self, ...params)
// } else if (!timer) {
// // 两次触发的间隔时间没有超哥wait,则设置定时器,让其等待remaining这么久之后执行一次「前提:没有设置过定时器」
// timer = setTimeout(() => {
// clearTimeout(timer);
// timer = null;
// previous = new Date()
// func.call(self, ...params)
// }, remaining)
// }
// }
}
// 否
else {
let timer = null
return function proxy(...params) {
// 是否设置过定时器函数
// 是:退出
if (timer) return;
// 否:没有设置过,则立即执行,且设置定时器标识截断
timer = setTimeout(function () {
func.call(self, ...params)
timer = null // 把定时器标识重置为null
}, wait)
}
}
}
function handle() {
console.log('ok')
}
document.body.style.height = '5000px'
window.onscroll = throttle(handle, true)
// window.onscroll = proxy;
// *待解决:借鉴:采用防抖立即执行;改造:加入一次执行
/**
* 1.网络经典valid,不会立即执行
* 2.不用setTimeout的实现方案
* 3.加入immediate控制参数
* 4.综合整理
*/
function throttle(func, wait) {
if (typeof func !== 'function') throw new TypeError('func must be a function');
if (typeof wait === 'undefined') wait = 500;
let timmer = null,
previous = 0; // 记录上一次操作的时间
return function proxy(...params) {
let self = this,
now = new Date(),// 当前这次触发操作的时间
remaining = wait - (now - previous)
if (remain <= 0) {
// 两次间隔时间,都超过wait了,直接执行即可
cleartTimeout(timer); // 兼容临界点
timer = null;
previous = now
func.call(self, ...params)
} else if (!timer) {
// 两次触发的间隔时间没有超哥wait,则设置定时器,让其等待remaining这么久之后执行一次「前提:没有设置过定时器」
timer = setTimeout(() => {
cleartTimeout(timer);
timer = null;
previous = new Date()
func.call(self, ...params)
}, remaining)
}
}
// let timmer = null
// return function proxy(...params) {
// if (timer) return;
// // 否:没有设置过,则立即执行,且设置定时器标识截断
// func.call(self, ...params)
// timer = setTimeout(function () {
// timer = null // 把定时器标识重置为null
// }, wait)
// }
}
function handle() {
console.log('ok')
}
window.onscroll = throttle(handle)
// window.onscroll = proxy;
/**
* 定时器
* timer = setTimeout(...)
* + 给timer赋值,timer是一个数字,存储当前是第几个定时器
* + cleartTimeout(timer),从系统中清除定时器,系统中没有定时器,但是timer变量的值还是那个数字呢
* + 最好timer重新赋值null
* 这样就可以基于timer是否为null了解到是否还有定时器了
*
* 排号 -》 纸条101 -》 办理业务 -》 系统中清除号码纸条还在你手里(撕掉它)
*
* 临界点:过了200ms定时器的时间到了,但是我们也触发了一次
* 让其执行一次,可以走理解执行,可以清除
*/