函数节流,简单地讲,就是让一个函数无法在很短的时间间隔内连续调用,只有当上一次函数执行后过了你规定的时间间隔,才能进行下一次该函数的调用。

函数节流的原理挺简单的,估计大家都想到了,那就是定时器。当我触发一个时间时,先setTimout让这个事件延迟一会再执行,如果在这个时间间隔内又触发了事件,那我们就clear掉原来的定时器,再setTimeout一个新的定时器延迟一会执行,就这样。

以下场景往往由于事件频繁被触发,因而频繁执行DOM操作、资源加载等重行为,导致UI停顿甚至浏览器崩溃。

1. window对象的resize、scroll事件

2. 拖拽时的mousemove事件

3. 射击游戏中的mousedown、keydown事件

4. 文字输入、自动完成的keyup事件

实际上对于window的resize事件,实际需求大多为停止改变大小n毫秒后执行后续处理;而其他事件大多的需求是以一定的频率执行后续处理。针对这两种需求就出现了debounce和throttle两种解决办法。

throttle 和 debounce 是解决请求和响应速度不匹配问题的两个方案。二者的差异在于选择不同的策略。

throttle 等时间 间隔执行函数。

debounce 时间间隔 t 内若再次触发事件,则重新计时,直到停止时间大于或等于 t 才执行函数。

一、throttle函数的简单实现

  1. function throttle(fn, threshhold, scope) {
  2. threshhold || (threshhold = 250);
  3. var last,
  4. timer;
  5. return function () {
  6. var context = scope || this;
  7. var now = +new Date(),
  8. args = arguments;
  9. if (last && now - last + threshhold < 0) {
  10. // hold on to it
  11. clearTimeout(deferTimer);
  12. timer = setTimeout(function () {
  13. last = now;
  14. fn.apply(context, args);
  15. }, threshhold);
  16. } else {
  17. last = now;
  18. fn.apply(context, args);
  19. }
  20. };
  21. }

调用方法

  1. $('body').on('mousemove', throttle(function (event) {
  2. console.log('tick');
  3. }, 1000));

二、debounce函数的简单实现

  1. function debounce(fn, delay) {
  2. var timer = null;
  3. return function () {
  4. var context = this,
  5. args = arguments;
  6. clearTimeout(timer);
  7. timer = setTimeout(function () {
  8. fn.apply(context, args);
  9. }, delay);
  10. };
  11. }

调用方法

  1. $('input.username').keypress(debounce(function (event){
  2. // do the Ajax request
  3. }, 250));

三、简单的封装实现

  1. /** * throttle * @param fn, wait, debounce */
  2. var throttle = function ( fn, wait, debounce ) {
  3. var timer = null, // 定时器
  4. t_last = null, // 上次设置的时间
  5. context, // 上下文
  6. args, // 参数
  7. diff; // 时间差
  8. return function () {
  9. var curr = + new Date(),
  10. context = this,
  11. args = arguments;
  12. clearTimeout( timer );
  13. if ( debounce ) { // 如果是debounce
  14. timer = setTimeout( function () {
  15. fn.apply( context, args );
  16. }, wait );
  17. }else{ // 如果是throttle
  18. if ( !t_last ) t_last = curr;
  19. if ( curr - t_last >= wait ) {
  20. fn.apply( context, wait );
  21. context = wait = null;
  22. }
  23. }
  24. }
  25. }/** * debounce * @param fn, wait */
  26.  
  27. var debounce = function ( fn, wait ) {
  28. return throttle( fn, wait, true );
  29. }
  1. 这两个方法适用于会重复触发的一些事件,如:mousemovekeydownkeyupkeypressscroll等。
    如果只绑定原生事件,不加以控制,会使得浏览器卡顿,用户体验差。为了提高js性能,建议在使用以上及类似事件的时候用函数节流或者函数去抖加以控制。
  2.  
  3. 四、underscore v1.7.0相关的源码剖析 
    1. _.throttle函数
  1. _.throttle = function(func, wait, options) {
  2. var context, args, result;
  3. var timeout = null;
  4. // 定时器
  5. var previous = 0;
  6. // 上次触发的时间
  7. if (!options) options = {};
  8. var later = function() {
  9. previous = options.leading === false ? 0 : _.now();
  10. timeout = null;
  11. result = func.apply(context, args);
  12. if (!timeout) context = args = null;
  13. };
  14. return function()
  15. {
  16. var now = _.now();
  17. // 第一次是否执行
  18. if (!previous && options.leading === false) previous = now;
  19. // 这里引入了一个remaining的概念:还剩多长时间执行事件
  20. var remaining = wait - (now - previous);
  21. context = this;
  22. args = arguments;
  23. // remaining <= 0 考虑到事件停止后重新触发或者
  24. // 正好相差wait的时候,这些情况下,会立即触发事件
  25. // remaining > wait 没有考虑到相应场景
  26. // 因为now-previous永远都是正值,且不为0,那么
  27. // remaining就会一直比wait小,没有大于wait的情况
  28. // 估计是保险起见吧,这种情况也是立即执行
  29. if (remaining <= 0 || remaining > wait)
  30. {
  31. if (timeout)
  32. {
  33. clearTimeout(timeout);
  34. timeout = null;
  35. }
  36. previous = now;
  37. result = func.apply(context, args);
  38. if (!timeout) context = args = null;
  39. // 是否跟踪
  40. } else if (!timeout && options.trailing !== false)
  41. {
  42. timeout = setTimeout(later, remaining);
  43. }
  44. return result;
  45. };
  46. };

由上可见,underscore考虑了比较多的情况:options.leading:

第一次是否执行,默认为true,表示第一次会执行,传入{leading:false}则禁用第一次执行

options.trailing:最后一次是否执行,默认为true,表示最后一次会执行,

传入{trailing: false}表示最后一次不执行所谓第一次是否执行,是刚开始触发事件时,要不要先触发事件,如果要,则previous=0,remaining 为负值,则立即调用了函数所谓最后一次是否执行,是事件结束后,最后一次触发了此方法,如果要执行,则设置定时器,即事件结束以后还要在执行一次。remianing > wait 表示客户端时间被修改过。

2. _.debounce函数 

  1. _.debounce = function(func, wait, immediate) {
  2. // immediate默认为false
  3. var timeout, args, context, timestamp, result;
  4. var later = function() {
  5. // 当wait指定的时间间隔期间多次调用_.debounce返回的函数,则会不断更新timestamp的值,导致last < wait && last >= 0一直为true,从而不断启动新的计时器延时执行func var last = _.now() - timestamp;
  6. if (last < wait && last >= 0) {
  7. timeout = setTimeout(later, wait - last);
  8. } else {
  9. timeout = null;
  10. if (!immediate) {
  11. result = func.apply(context, args);
  12. if (!timeout) context = args = null;
  13. }
  14. }
  15. };
  16. return function()
  17. {
  18. context = this;
  19. args = arguments;
  20. timestamp = _.now();
  21. // 第一次调用该方法时,且immediate为true,则调用func函数
  22. var callNow = immediate && !timeout; // 在wait指定的时间间隔内首次调用该方法,则启动计时器定时调用func函数
  23. if (!timeout) timeout = setTimeout(later, wait);
  24. if (callNow) {
  25. result = func.apply(context, args);
  26. context = args = null;
  27. }
  28. return result;
  29. };
  30. };

_.debounce实现的精彩之处我认为是通过递归启动计时器来代替通过调用clearTimeout来调整调用func函数的延时执行。

  1.  

JavaScript性能优化之函数节流(throttle)与函数去抖(debounce)的更多相关文章

  1. [JavaScript] 函数节流(throttle)和函数防抖(debounce)

    js 的函数节流(throttle)和函数防抖(debounce)概述 函数防抖(debounce) 一个事件频繁触发,但是我们不想让他触发的这么频繁,于是我们就设置一个定时器让这个事件在 xxx 秒 ...

  2. javascript 函数节流 throttle 解决函数被频繁调用、浏览器卡顿的问题

    * 使用setTimeout index.html <html> <head> <meta charset="UTF-8"> <title ...

  3. JS中的函数节流throttle详解和优化

    JS中的函数节流throttle详解和优化在前端开发中,有时会为页面绑定resize事件,或者为一个页面元素绑定拖拽事件(mousemove),这种事件有一个特点,在一个正常的操作中,有可能在一个短的 ...

  4. JavaScript性能优化

    如今主流浏览器都在比拼JavaScript引擎的执行速度,但最终都会达到一个理论极限,即无限接近编译后程序执行速度. 这种情况下决定程序速度的另一个重要因素就是代码本身. 在这里我们会分门别类的介绍J ...

  5. 摘:JavaScript性能优化小知识总结

    原文地址:http://www.codeceo.com/article/javascript-performance-tips.html JavaScript的性能问题不容小觑,这就需要我们开发人员在 ...

  6. JavaScript性能优化小窍门汇总(含实例)

    在众多语言中,JavaScript已经占有重要的一席之地,利用JavaScript我们可以做很多事情 , 应用广泛.在web应用项目中,需要大量JavaScript的代码,将来也会越来越多.但是由于J ...

  7. js 函数节流throttle 函数去抖debounce

    1.函数节流throttle 通俗解释: 假设你正在乘电梯上楼,当电梯门关闭之前发现有人也要乘电梯,礼貌起见,你会按下开门开关,然后等他进电梯: 但是,你是个没耐心的人,你最多只会等待电梯停留一分钟: ...

  8. JavaScript性能优化小知识总结(转)

    JavaScript的性能问题不容小觑,这就需要我们开发人员在编写JavaScript程序时多注意一些细节,本文非常详细的介绍了一下JavaScript性能优化方面的知识点,绝对是干货. 前言 一直在 ...

  9. JavaScript性能优化篇js优化

    JavaScript性能优化篇js优化   随着Ajax越来越普遍,Ajax引用的规模越来越大,Javascript代码的性能越来越显得重要,我想这就是一个很典型的例子,上面那段代码因为会被频繁使用, ...

  10. 函数节流throttle和防抖debounce

    throttle 函数节流 不论触发函数多少次,函数只在设定条件到达时调用第一次函数设定,函数节流 1234567891011 let throttle = function(fn,intervalT ...

随机推荐

  1. Java double和 float丢失精度问题

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt357 由于对float或double 的使用不当,可能会出现精度丢失的问题. ...

  2. Linux-mknod命令(9)

    mknod命令用于创建字符设备文件和块设备文件 (ls /dev -l 结果显示b开头和c开头的,即标识了块设备和字符设备.) 为了管理设备,所以设备中都有两个设备号:  主设备号:为了区分不同类型的 ...

  3. 五个数据段之代码段、数据段、BSS、栈、堆

    继上文讲完了对内存管理的一些知识,下面笔者再对上篇文章的内容加以拓展,那么我们今天就来说一说5个数据段 五个数据段 进程(执行的程序)会占用一定数量的内存,它或是用来存放磁盘载入的程序代码,或是存放取 ...

  4. 集美大学网络1413第九次作业成绩(团队五) -- 测试与发布(Alpha版本)

    NO.NE团队的项目链接有效,六个核桃和六指神功团队可以请教下他们,避免因IP地址无效或者因tomcat不打开就不能访问的情况,毕竟助教没办法知道此时此刻它是开着还是关闭啊啊啊... 题目 团队作业5 ...

  5. 结对编程1-四则运算GUI实现(58、59)

    题目描述 我们在个人作业1中,用各种语言实现了一个命令行的四则运算小程序.进一步,本次要求把这个程序做成GUI(可以是Windows PC 上的,也可以是Mac.Linux,web,手机上的),成为一 ...

  6. 个人作业3——个人总结(Alphe)

    小结: 1.软件工程的第一阶段终于结束了,说实话,每个人的课程都很紧张,在这么紧张的时期我们都每周抽出一些时间来开个小会总结或者计划软件工程的相关任何非常难得,大家的态度都诚恳认真,我亦是如此,只是我 ...

  7. 团队作业4——第一次项目冲刺(Alpha版本)第一天 and 第二天

    第一天冲刺 一.Daily Scrum Meeting照片 二.每个人的工作 1.今天计划完成的任务 徐璨 申悦:查找关于安卓开发资料,环境搭建 连永刚 林方言:设计项目所要实现的功能,并对功能进行详 ...

  8. 201521123013 《Java程序设计》第10周学习总结

    1. 本章学习总结 2. 书面作业 Q1.finally题目4-2 1.1 截图你的提交结果(出现学号) 1.2 4-2中finally中捕获异常需要注意什么? finally块中的异常必须在fina ...

  9. 201521123033《Java程序设计》第14周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多数据库相关内容. 2. 书面作业 1. MySQL数据库基本操作 建立数据库,将自己的姓名.学号作为一条记录插入.(截图,需出现自 ...

  10. 201521123024 java 第十周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 finally 题目4-2 1.1 截图你的提交结果(出现学 ...