手写防抖debounce函数
当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间
当事件密集触发时,函数的触发会被频繁的推迟
只有等待了一段时间也没有事件触发,才会真正的执行响应函数
防抖的应用场景很多:
输入框中频繁的输入内容,搜索或者提交信息;
频繁的点击按钮,触发某个事件;
监听浏览器滚动事件,完成某些特 定操作;
用户缩放浏览器的resize事件;
大概编写
防抖函数的核心就是 每次函数运行都要等待一定时间才能运行,而当上次运行还没有开始就又来了新的执行函数,就立刻清除上一次的计时器,重新计时。
function debounce(fn,delay){
//定义一个定时器,保存上一次的定时器
let timer = null
//真正的执行函数
const _debounce = function(){
if(timer) clearTimeout(timer)
timer = setTimeout(()=>{
fn()
},delay)
}
return _debounce
}
添加this参数的指向
function debounce(fn,delay){
let timer = null
const _debounce = function(...args){
if(timer) clearTimeout(timer)
timer = setTimeout(()=>{
fn.apply(this,args)
},delay);
}
return _debounce
}
当第一次执行函数的时候,立即执行
传入第三个参数immediate,初始化为false,表示不立即执行,当传入true后,表示当进行计数器执行一次函数后,下次的函数执行立即执行
function debounce(fn,delay,immediate = false){
let timer = null
//标记是否为一个周期内的第一次执行
let isInvoke = false
cosnt _debounce = function(...args){
if(timer) clearTimeout(timer)
//判断如果需要立即执行,并且还没有被执行,就直接执行
if(immediate && !isInvoke){
fn.apply(this,args)
isInvoke = true
}else{
timer = setTimeout(()=>{
fn.apply(this,args)
isInvoke = false
},delay)
}
}
return _debounce
}
取消功能
用户可以进行取消
function debounce(fn,delay,immediate = false){
let timer = null
let isInvoke = false
const _debounce = function(...args){
if(timer) clearTimeout(timer)
if(immediate && !isInovke){
fn.apply(this,args)
isInvoke = true
}else{
timer = setTimout(()=>{
fn.apply(this,args)
isInvoke = false
},delay)
}
}
_debounce.cancel = function(){
if(timer) clearTimeout(timer)
timer = null
isInvoke = false
}
return _debounce
}
函数返回值
function debounce(fn, delay, immediate = false, resultCallback) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null
let isInvoke = false
// 2.真正执行的函数
const _debounce = function(...args) {
return new Promise((resolve, reject) => {
// 取消上一次的定时器
if (timer) clearTimeout(timer)
// 判断是否需要立即执行
if (immediate && !isInvoke) {
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
resolve(result)
isInvoke = true
} else {
// 延迟执行
timer = setTimeout(() => {
// 外部传入的真正要执行的函数
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
resolve(result)
isInvoke = false
timer = null
}, delay)
}
})
}
// 封装取消功能
_debounce.cancel = function() {
if (timer) clearTimeout(timer)
timer = null
isInvoke = false
}
return _debounce
}
手写节流throttle函数
大概编写
function throttle(fn,interval){
//lastTime 表示上次执行时候的时间
let lastTime = 0
const _throttle = function(){
//nowTime 表示当前的时间
let nowTime = new Date().getTime()
//remainTime 表示在设置的节流间隔内,距离下次执行时间还剩余的时间
let remainTime = interval - (nowTime - lastTime)
//当剩余时间<=0的时候,表示可以执行,并且更新lastTime到当前时间
if(remainTime <= 0 ){
fn()
lastTime = nowTime
}
}
return _throttle
}
添加功能 可以选择是否立即执行
在调用函数的时候,添加一个参数trading,表示在第一次执行函数的时候是否就立刻执行
其实上面的大概编写代码中,已经可以实现 立即执行。因为初始的lastTime为0,但是nowTime为很大的数值,这个时候的remainTime一定小于0,那么在第一时间内就会立刻进行函数的调用。
但是现在要做的事情是,需要加上一个参数,让进行函数调用的时候可以选择是否立即执行
代码如下,其实就是加上一次判断
//初始时候的leading 设置为true
function throttle(fn,interval,leading = true){
let lastTime = 0
const _throttle = function(){
//进行是否需要立即执行判断 当lastTime=0表示是第一次,leading为false表示不需要
if(!lastTime && !leading) lastTime = nowTime
let nowTime = new Date().getTime()
let remainTime = interval - (nowTime - lastTime)
if(remainTime <= 0 ){
fn()
}
}
return _throttle
}
添加功能 函数最后是否需要调用执行
调用函数的时候传递参数 trading参数,表示是否需要最后一次函数调用执行
核心思想是 需要在最后一次函数调用后 启动一个计时器,计时器的时间就是距离下次要执行的时间间隔
function throttle(fn,interval,options = {leading:true,trading:false}){
const {leading,trading} = options
let lastTime = 0
let timer = null
const _throttle = function(){
const nowTime = new Date().getTime()
if(!lastTime && !leading) lastTime = nowTime
const remainTime = interval - (nowTime-lastTime)
if(remainTime <= 0){
if(timer){
clearTimeout(timer)
timer = null
}
fn()
lastTime = nowTime
//已经完成一次执行,就不需要加定时器了
return
}
if(trading && !timer){
timer = setTimeout(()=>{
//通过定时器执行完成以后,要将timer置为0防止后面又需要执行
timer = null
//对lastTime进行重置的时候,需要注意,如果此时需要进行第一次输出,那就直接为0,
//否则需要设置为当前时间,从而防止第一次执行
lastTime = !leading ? 0:new Date().getTime()
fn()
},remainTime)
}
}
return _throttle
}
添加取消函数
function throttle(fn, interval, options = { leading: true, trailing: false }) {
// 1.记录上一次的开始时间
const { leading, trailing, resultCallback } = options
let lastTime = 0
let timer = null
// 2.事件触发时, 真正执行的函数
const _throttle = function(...args) {
return new Promise((resolve, reject) => {
// 2.1.获取当前事件触发时的时间
const nowTime = new Date().getTime()
if (!lastTime && !leading) lastTime = nowTime
// 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长事件需要去触发函数
const remainTime = interval - (nowTime - lastTime)
if (remainTime <= 0) {
if (timer) {
clearTimeout(timer)
timer = null
}
// 2.3.真正触发函数
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
resolve(result)
// 2.4.保留上次触发的时间
lastTime = nowTime
return
}
if (trailing && !timer) {
timer = setTimeout(() => {
timer = null
lastTime = !leading ? 0: new Date().getTime()
const result = fn.apply(this, args)
if (resultCallback) resultCallback(result)
resolve(result)
}, remainTime)
}
})
}
//为_throttle 绑定取消事件
_throttle.cancel = function() {
if(timer) clearTimeout(timer)
timer = null
lastTime = 0
}
return _throttle
}