前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JS防抖与节流(类比游戏技能)

JS防抖与节流(类比游戏技能)

作者头像
小城故事
发布2023-02-27 16:06:48
1.1K0
发布2023-02-27 16:06:48
举报
文章被收录于专栏:XC's Blog 日常笔记

JS防抖与节流(类比游戏技能)

点击获取资料–2021最新前端面试题汇总

防抖

生活中防抖的例子

  • 酒店的自动开关门
    • 感应到人自动开门,5s后自动关闭
    • 如果5s的倒计时里有人来,那么这个5s重新开始计时

类比技能吟唱

  • 知道释放一个法术(魔法)是需要吟唱时间的
    • 比如死歌的R
    • 比如波比的R
  • 假设的技能没有CD
    • 那么在技能还没有吟唱完的时候再一次使用技能会打断第一次施法,重新开始施法
    • 在上次技能没释放的时候释放下一次技能,第一次施法被打断是很好理解的吧
    • 重新施法,重新开始吟唱也是很好理解的吧

js中的防抖

  • 如键盘输入等

就和上面两个例子中提到的一样。 主要是用到了三点

  • addEventListener
  • setTimeout
  • clearTimeout

基础的html代码如下

代码语言:javascript
复制
const button = document.querySelector('#input')
function input () {
console.log('input!');
}
function debounce (fn,delay) {
// Todo: 完成防抖
}
button.addEventListener('click', debounce(input,1000))
  • 因为防抖需要知道延时时间和完成的事件,所以防抖有两个参数
1.使用高阶函数,避免添加响应的时候触发函数
  • 一步一步来,如果没有使用高阶函数而是直接调用函数,如下图,会在添加响应的时候触发fn()
代码语言:javascript
复制
function debounce (fn,delay) {
  fn()
}

所以先改为高阶函数解决这个bug

代码语言:javascript
复制
function debounce (fn,delay) {
  return function () {
    fn()
  }
}
2.使用setTimeout完成延时执行,clearTimeout来控制刷新延时
2.1 为什么要把timer定义在函数外面?

为了形成闭包,这样在函数内部才能保证clearTimeout掉timer

代码语言:javascript
复制
function debounce (fn, delay) {
  let timer
  return function () {
    clearTimeout(timer)
    timer = setTimeout(function () {
      fn()
    }, delay)
  }
}
3.并不是万事大吉,注意this指针的改变

在函数里面log一下this指针

代码语言:javascript
复制
const button = document.querySelector('#input')
function input () {
  console.log('input!');
  console.log(this);
}
function debounce (fn, delay) {
  let timer
  return function () {
    clearTimeout(timer)
    timer = setTimeout(function () {
      fn()
    }, delay)
  }
}
button.addEventListener('click', debounce(input, 300))

结果函数内部的this指针变成了window,这是肯定不行的

3.1 用context保存this指针,args保存添加的参数,用apply来改变this指针的指向
代码语言:javascript
复制
function debounce (fn, delay) {
    let timer
    return function () {
        let context = this
        let args=arguments
        clearTimeout(timer)
        timer = setTimeout(function () {
            fn.apply(context,args)
        }, delay)
    }
}

成功得到input的this指针

4.return的function不可以使用匿名函数

其实是可以用匿名函数来简化上面的代码的,但是只能简化setTimeout里面的函数

  • 需要拿到function里面的context上下文
  • 因为匿名函数没有this指针,所以return的函数写成匿名函数会出错,代码如下
代码语言:javascript
复制
function debounce (fn, delay) {
     let timer
     return () => {
       let context = this
       let args=arguments
       clearTimeout(timer)
       timer = setTimeout(() => {
         fn.apply(context,args)
       }, delay)
     }
}

这种写法,this指针会指向window 最终代码

代码语言:javascript
复制
function debounce (fn, delay) {
   let timer
   return function () {
     let context = this
     let args = arguments
     clearTimeout(timer)
     timer = setTimeout(() => {
       fn.apply(context, args)
     }, delay)
   }
}

节流

生活中节流的例子

  • 鲸鱼到水面换气
    • 鲸鱼是哺乳动物,所以需要每隔一段时间到水面换气
    • 不一直在水面,因为还需要下潜保持皮肤的水分,这就和节约资源类似
    • 鲸鱼呷一口气,这口气能支撑1个小时,那鲸鱼就下潜了,1小时后再来换气
    • 这就是节流

类比平A僵直

打过王者荣耀的都知道吧,拆塔的时候疯狂按攻击键?这没多大用

你的攻击次数由你的攻击速度(攻击间隔时长)决定

就是在上一次攻击指令完成前,是没有办法进行下一次攻击指令的

平A的僵直就是节流

js中的节流

  • 如滚动监听

在上面防抖基础上,节流的介绍就不再那么啰嗦,直接开始编写节流的代码

1.使用定时器的思想来完成
1.1根据思路写出基础代码
  • 同样是高阶函数
  • 同样要操作timer,timer闭包
  • 如果上一次还没延时没结束,就啥也不做(判断timer是否存在即可)。完成节流任务
代码语言:javascript
复制
function throttle (fn, delay) {
  let timer
  return function () {
    if (timer) return
    timer = setTimeout(() => {
      fn()
      timer = null
    }, delay)
  }
}

1.2关键点还是改变this的指向

代码语言:javascript
复制
function throttle (fn, delay) {
      let timer
      return function () {
        let context = this
        let args = arguments
        if (timer) return
        timer = setTimeout(() => {
          fn.apply(context, args)
          timer = null
        }, delay)
      }
    }
2.使用Date对象来实现
2.1综合前面的问题,直接写出来
  • 但是发现有一个问题,就是函数第一次会立即执行
代码语言:javascript
复制
function throttle (fn, delay) {
  let pre = 0
  return function () {
    let now = new Date()
    let context = this
    let args = arguments
    if (now - pre > delay) {
      fn.apply(context, args)
      pre = now
    }
  }
}

2.2可以第一次不立即执行吗?可以!

判断,如果!pre就是第一次,第一次先拿到当前时间就可以了

代码语言:javascript
复制
function throttle (fn, delay) {
  let pre = 0
  return function () {
    if (!pre) pre = new Date()
    let now = new Date()
    let context = this
    let args = arguments
    if (now - pre > delay) {
      fn.apply(context, args)
      pre = now
    }
  }
}
3.讨论一下前两种方案的弊端
  • 假设有两次请求,第一次正常,第二次被节流了
  • 最后只执行了一次,这是不愿意看到的!
  • 如何解决?请看第4点
4.Date结合定时器解决3.的弊端
  • 在2中代码的基础上来添加
  • 闭包的timer,如果在节流时间内,就定一个定时器来完成被吃掉的请求
  • 定时为剩下的时间remainTime=delay-(now-pre),定时前记得判断是否存在(存在就不用设置直接return就可以了),完成后记得要更新pre和timer=null
代码语言:javascript
复制
function throttle (fn, delay) {
  let pre = 0
  let timer
  return function () {
    if (!pre) pre = new Date()
    let now = new Date()
    let context = this
    let args = arguments
    let remainTime = delay - (now - pre)
    if (now - pre > delay) {
      fn.apply(context, args)
      pre = now
    } else {
      if (timer) return
      timer = setTimeout(() => {
        fn.apply(context, args)
        pre = now
        timer = null
      }, remainTime)
    }
  }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JS防抖与节流(类比游戏技能)
    • 防抖
      • 生活中防抖的例子
        • 类比技能吟唱
          • js中的防抖
            • 1.使用高阶函数,避免添加响应的时候触发函数
            • 2.使用setTimeout完成延时执行,clearTimeout来控制刷新延时
            • 3.并不是万事大吉,注意this指针的改变
            • 4.return的function不可以使用匿名函数
        • 节流
          • 生活中节流的例子
            • 类比平A僵直
              • js中的节流
                • 1.使用定时器的思想来完成
                • 2.使用Date对象来实现
                • 3.讨论一下前两种方案的弊端
                • 4.Date结合定时器解决3.的弊端
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档