资讯专栏INFORMATION COLUMN

前端进击的巨人(八):浅谈函数防抖与节流

_Zhao / 2461人阅读

摘要:隆重请出主角防抖与节流。防抖与节流的异同相同都是防止某一时间段内,函数被频繁调用执行,通过时间频率控制,减少回调函数执行次数,来实现相关性能优化。参考文章分钟理解的节流防抖及使用场景函数防抖和节流

本篇课题,或许早已是烂大街的解读文章。不过春招系列面试下来,不少伙伴们还是似懂非懂地栽倒在(~面试官~)深意的笑容之下,权当温故知新。

JavaScript的执行过程,是基于栈来进行的。复杂的程序代码被封装到函数中,程序执行时,函数不断被推入执行栈中。所以 "执行栈" 也称 "函数执行栈"

函数中封装的代码块,一般都有相对复杂的逻辑处理(计算/判断),例如函数中可能会涉及到 DOM 的渲染更新,复杂的计算与验证, Ajax 数据请求等等。

前端页面的操作权,大部分都是属于浏览端的客户爸爸们(单身三十年的手速,惹不起惹不起!!!)。如果函数被频繁调用,造成的性能开销绝对不只一点点。

前: DOM 频繁重绘的卡顿让客户爸爸们想把你揪出来一顿大招。。。

后: 后端同学正在提刀赶来的路上:“为什么我的接口被你玩挂了”。。。

既要提升用户体验,又要减少后端服务开销,可见我们大前端的使命不只一页PPT。说好前因,接着就是后果了。既然有优化的需求,必然就要有相应的解决方案。隆重请出主角: “防抖”“节流”

防抖(debounce)
在事件被触发 n 秒后再执行回调函数,如果在这 n 秒内又被触发,则重新计时延迟时间。

生活化理解:英雄的技能条,技能条读完才能使用技能(R大招60s)

防抖的实现方式分两种 “立即执行”“非立即执行”,区别在于第一次触发时,是否立即执行回调函数。

非立即执行
”非立即执行防抖“ 指事件触发后,回调函数不会立即执行,会在延迟时间 n 秒后执行,如果 n 秒内被调用多次,则重新计时延迟时间
// e.g. 防抖 - 非立即执行
function debounce(func, delay) {
  var timeout;
  return function() {
    var context = this;
    var args = arguments;
    // && 短路运算 == if(timeout) else {...} 
    timeout && clearTimeout(timeout); 
    timeout = setTimeout(function(){
      func.apply(context, args);
    }, delay);
  }
}

// 调用
var printUserName = debounce(function(){ 
  console.log(this.value);
}, 800);
document.getElementById("username")
  .addEventListener("keyup", printUserName);
立即执行
“立即执行防抖” 指事件触发后,回调函数会立即执行,之后要想触发执行回调函数,需等待 n 秒延迟
// e.g. 防抖 - 立即执行
function debounce(func, delay) {
    var timeout;
    return function() {
        var context = this;
        var args = arguments;
        callNow = !timeout;
        timeout = setTimeout(function() {
            timeout = null;
        }, delay);
        callNow && func.apply(context, args);
    }
}

函数防抖原理:通过维护一个定时器,其延迟计时以最后一次触发为计时起点,到达延迟时间后才会触发函数执行。

节流(throttle)
规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效(间隔执行)

生活化理解:

FPS射击游戏子弹射速(即使按住鼠标左键,射出子弹的速度也是限定的)

水龙头的滴水(水滴攒到一定重量才会下落)

函数节流实现的方式有 “时间戳”“定时器” 两种。

时间戳
// e.g. 节流 - 时间戳
function throttle(func, delay) {
  var lastTime = 0;
  return function() {
    var context = this;
    var args = arguments;
    var nowTime = +new Date();
    if (nowTime > lastTime + delay) {
      func.apply(context, args)
      lastTime = nowTime;
    }
  }
}

“时间戳” 的方式,函数在时间段开始时执行。

缺点:假定函数间隔1s执行,如果最后一次停止触发,卡在4.2s,则不会再执行。

定时器
// e.g. 节流 - 定时器
function throttle(func, delay) {
  var timeout;
  return function() {
    var context = this;
    var args = arguments;
    if (!timeout) {
      setTimeout(function(){
        func.apply(context, args);
        timeout = null;
      }, delay)
    }
  }
}

“定时器” 的方式,函数在时间段结束时执行。可理解为函数并不会立即执行,而是等待延迟计时完成才执行。(由于定时器延时,最后一次触发后,可能会再执行一次回调函数)

时间戳 + 定时器(互补优化)
// e.g. 节流 - 时间戳 + 定时器
function throttle(func, delay) {
  let lastTime, timeout;
  return function() {
    let context = this;
    let args = arguments;
    let nowTime = +new Date();
    if (lastTime && nowTime < lastTime + delay) {
      timeout && clearTimeout(timeout);
      timeout = setTimeout(function(){
        lastTime = nowTime;
        func.apply(context, args);
      }, delay);
    } else {
      lastTime = nowTime;
      func.apply(context, args);
    }
  }
}

合并优化的原理:“时间戳”方式让函数在时间段开始时执行(第一次触发立即执行),“定时器”方式让函数在最后一次事件触发后(如4.2s)也能触发。

函数节流原理:一定时间内只触发一次,间隔执行。通过判断是否到达指定触发时间,间隔时间固定。

“防抖” 与 “节流” 的异同

相同:都是防止某一时间段内,函数被频繁调用执行,通过时间频率控制,减少回调函数执行次数,来实现相关性能优化。

区别:“防抖”是某一时间内只执行一次,最后一次触发后过段时间执行,而“节流”则是间隔时间执行,间隔时间固定。

“防抖” 与 “节流” 的应用场景 防抖

文本输入搜索联想

文本输入验证(包括 Ajax 后端验证)

节流

鼠标点击

监听滚动 scroll

窗口 resize

mousemove 拖拽

应用场景还有很多,具体场景需具体分析。只要涉及高频的函数调用,都可参考函数防抖节流的优化方案。

鼓起勇气写在结尾:以上代码都不是 “完美” 的 “防抖 / 节流” 实现代码!!!仅就实现方式和基本原理,浅谈分解一二。

实际代码开发中,一般会引入lodash 相对 “靠谱” 的第三方库,帮我们去实现防抖节流的工具函数。有兴趣的伙伴们可阅读 lodash 相关源码,加深印象理解可再读以下参考文章。

参考文章

7分钟理解JS的节流、防抖及使用场景

函数防抖和节流

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102343.html

相关文章

  • 高级函数技巧-函数抖与节流

    摘要:封装方法也比较简单,书中对此问题也进行了处理使用定时器,让函数延迟秒后执行,在此秒内,然后函数再次被调用,则删除上次的定时器,取消上次调用的队列任务,重新设置定时器。 在实际开发中,函数一定是最实用最频繁的一部分,无论是以函数为核心的函数式编程,还是更多人选择的面向对象式的编程,都会有函数的身影,所以对函数进行深入的研究是非常有必要的。 函数节流 比较直白的说,函数节流就是强制规定一...

    whinc 评论0 收藏0
  • 小菊花课堂之JS抖与节流

    摘要:文章来源详谈防抖和节流轻松理解函数节流和函数防抖函数防抖和节流好啦,今天的小菊花课堂之的防抖与节流的内容就告一段落啦,感各位能耐心看到这里。 前言 陆游有一首《冬夜读书示子聿》——古人学问无遗力,少壮工夫老始成。纸上得来终觉浅,绝知此事要躬行。,其中的意思想必大家都能明白,在学习或工作中,不断的印证着这首诗的内涵。所以,又有了此篇小菊花文章。 详解 在前端开发中,我们经常会碰到一些会持...

    leoperfect 评论0 收藏0
  • 小菊花课堂之JS抖与节流

    摘要:文章来源详谈防抖和节流轻松理解函数节流和函数防抖函数防抖和节流好啦,今天的小菊花课堂之的防抖与节流的内容就告一段落啦,感各位能耐心看到这里。 前言 陆游有一首《冬夜读书示子聿》——古人学问无遗力,少壮工夫老始成。纸上得来终觉浅,绝知此事要躬行。,其中的意思想必大家都能明白,在学习或工作中,不断的印证着这首诗的内涵。所以,又有了此篇小菊花文章。 详解 在前端开发中,我们经常会碰到一些会持...

    Yangder 评论0 收藏0
  • 前端进击巨人(五):学会函数柯里化(curry)

    摘要:函数柯里化是把支持多个参数的函数变成接收单一参数的函数,并返回一个函数能接收处理剩余参数,而反柯里化就是把参数全部释放出来。但在一些复杂的业务逻辑封装中,函数柯里化能够为我们提供更好的应对方案,让我们的函数更具自由度和灵活性。 showImg(https://segmentfault.com/img/bVburN1?w=800&h=600); 柯里化(Curring, 以逻辑学家Has...

    chengtao1633 评论0 收藏0
  • 前端笔记(二) 对象深浅拷贝,函数抖与节流函数柯里化 ,图片预加载与懒加载

    摘要:对象是无法通过这种方式深拷贝。这就是函数防抖和节流要做的事情。函数防抖当触发频率过高时函数基本停止执行而函数节流则是按照一定的频率执行事件。 对象的深浅拷贝 对象的深拷贝与浅拷贝的区别: 浅拷贝:仅仅复制对象的引用, 而不是对象本身。 深拷贝:把复制的对象所引用的全部对象都复制一遍 浅拷贝的实现: var obj = { age : 18, person : { ...

    dongxiawu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<