何为去抖函数?在学习JavaScript去抖函数之前我们需要先弄明白这个概念。很多人都会把去抖跟节流两个概念弄混,但是这两个概念其实是很好理解的。

去抖函数(Debounce Function),是一个可以限制指定函数触发频率的函数。我们可以理解为连续调用同一个函数多次,只得到执行该函数一次的结果;但是隔一段时间再次调用时,又可以重新获得新的结果,具体这段时间有多长取决于我们的设置。这种函数的应用场景有哪些呢?

比如我们写一个DOM事件监听函数,

window.onscroll = function(){
console.log('Got it!');
}

  

现在当我们滑动鼠标滚轮的时候,我们就可以看到事件被触发了。但是我们可以发现在我们滚动鼠标滚轮的时候,我们的控制台在不断的打印消息,因为window的scroll事件被我们不断的触发了。

在当前场景下,可能这是一个无伤大雅的行为,但是可以预见到,当我们的事件监听函数(Event Handler)涉及到一些复杂的操作时(比如Ajax请求、DOM渲染、大量数据计算),会对计算机性能产生多大影响;在一些比较老旧的机型或者较低版本的浏览器(尤其IE)中,很可能会导致死机情况的出现。所以这个时候我们就要想办法,在指定时间段内,只执行一定次数的事件处理函数。

理解去抖函数

说了一些概念和应用场景,但是还是很拗口,到底什么是去抖函数?

我们可以通过如下实例来理解:

假设有以下代码:

var debounce = function(callback, delay, immediate){
var timeout, result;
return function(){
var callNow;
if(timeout)
clearTimeout(timeout);
callNow = !timeout && immediate;
if(callNow) {
result = callback.apply(this, Array.prototype.slice.call(arguments, 0));
timeout = {};
}
else {
timeout = setTimeout(()=>{
callback.apply(this, Array.prototype.slice.call(arguments, 0));
}, delay);
}
};
};
var s = debounce(()=>{
console.log('yes...');
}, 2000);
window.onscroll = s;

  

debounce函数就是我自己实现的一个简单的去抖函数,我们可以通过这段代码进行实验。

步骤如下:

  • 复制以上代码,打开浏览器,打开控制台(F12),然后粘贴代码并回车执行。
  • 连续不断的滚动鼠标,查看控制台有无输出。
  • 停止滚动鼠标,2s之内再次滚动鼠标,查看是否有输出。
  • 连续滚动之后停止2s以上,查看是否有输出。

通过以上步骤,我们可以发现当我们连续滚动鼠标时,控制台没有消息被打印出来,停止2s以内并再次滚动时,也没有消息输出;但是当我们停止的时间超过2s时,我们可以看到控制台有消息输出。

这就是去抖函数。在连续的触发中(无论时长),只能得到触发一次的效果。在指定时间长度内连续触发,最多只能得到一次触发的效果。

underscore的实现

underscore源码如下(附代码注释):

// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
//去抖函数,传入的函数在wait时间之后(或之前)执行,并且只会被执行一次。
//如果immediate传递为true,那么在函数被传递时就立即调用。
//实现原理:涉及到异步JavaScript,多次调用_.debounce返回的函数,会一次性执行完,但是每次调用
//该函数又会清空上一次的TimeoutID,所以实际上只执行了最后一个setTimeout的内容。
_.debounce = function (func, wait, immediate) {
var timeout, result; var later = function (context, args) {
timeout = null;
//如果没有传递args参数,那么func不执行。
if (args) result = func.apply(context, args);
}; //被返回的函数,该函数只会被调用一次。
var debounced = restArgs(function (args) {
//这行代码的作用是清除上一次的TimeoutID,
//使得如果有多次调用该函数的场景时,只执行最后一次调用的延时。
if (timeout) clearTimeout(timeout);
if (immediate) {
////如果传递了immediate并且timeout为空,那么就立即调用func,否则不立即调用。
var callNow = !timeout;
//下面这行代码,later函数内部的func函数注定不会被执行,因为没有给later传递参数。
//它的作用是确保返回了一个timeout。
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(this, args);
} else {
//如果没有传递immediate,那么就使用_.delay函数延时执行later。
timeout = _.delay(later, wait, this, args);
} return result;
}); //该函数用于取消当前去抖效果。
debounced.cancel = function () {
clearTimeout(timeout);
timeout = null;
}; return debounced;
};

  

可以看到underscore使用了闭包的方法,定义了两个私有属性:timeout和result,以及两个私有方法later和debounced。最终会返回debounced作为处理之后的函数。timeout用于接受并存储setTimeout返回的TimeoutID,result用于执行用户传入的func函数的执行结果,later方法用于执行传入的func函数。

实现原理

利用了JavaScript的异步执行机制,JavaScript会优先执行完所有的同步代码,然后去事件队列中执行所有的异步任务。

当我们不断的触发debounced函数时,它会不断的clearTimeout(timeout),然后再重新设置新的timeout,所以实际上在我们的同步代码执行完之前,每次调用debounced函数都会重置timeout。所以异步事件队列中的异步任务会不断刷新,直到最后一个debounced函数执行完。只有最后一个debounced函数设置的later异步任务会在同步代码执行之后被执行。

所以当我们在之前实验中不断的滚动鼠标时,实际上是在不断的调用debounced函数,不断的清除timeout对应的异步任务,然后又设置新的timeout异步任务。当我们停止的时间不超过2s时,timeout对应的异步任务还没有被触发,所以再次滚动鼠标触发debounced函数还可以清除timeout任务然后设置新的timeout任务。一旦停止的时间超过2s,最终的timeout对应的异步代码就会被执行。

总结

  • 去抖是限制函数执行频率的一种方法。
  • 去抖后的函数在指定时间内最多被触发一次,连续触发去抖后的函数只能得到一次的触发效果。
  • underscore去抖的实现依赖于JavaScript的异步执行机制,优先执行同步代码,然后执行事件队列中的异步代码。

参考

更多underscore源码解读:GitHub

理解JavaScript中的去抖函数的更多相关文章

  1. 深入理解javascript中的立即执行函数(function(){…})()

    投稿:junjie 字体:[增加 减小] 类型:转载 时间:2014-06-12 我要评论 这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是 ...

  2. 深入理解javascript中的立即执行函数

    这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是用(function(){…})()包住业务代码,使用jquery时比较常见,需要的朋友可以 ...

  3. 【转】深入理解javascript中的立即执行函数(function(){…})()

    javascript和其他编程语言相比比较随意,所以javascript代码中充满各种奇葩的写法,有时雾里看花,当然,能理解各型各色的写法也是对javascript语言特性更进一步的深入理解. ( f ...

  4. 理解javascript中的立即执行函数(function(){})()

    之前看了好多代码,都有用到这种函数的写法,但是都没认真的去想为什么会这样写,今天开始想学习下jquery的源码,发现jquery也是使用这种方式,用(function(window, undefine ...

  5. 理解javascript中的立即执行函数(function(){})()(转)

    原文:https://www.cnblogs.com/yanzp/p/6371292.html

  6. javascript中的立即执行函数(function(){…})()

    javascript中的立即执行函数(function(){…})() 深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是用(function(){…})()包 ...

  7. 理解javascript中的回调函数(callback)【转】

    在JavaScrip中,function是内置的类对象,也就是说它是一种类型的对象,可以和其它String.Array.Number.Object类的对象一样用于内置对象的管理.因为function实 ...

  8. 如何理解JavaScript中的函数

    转: 如何理解JavaScript中的函数 JS中的函数简介 JS中的函数是一种通过调用来完成具体业务的一段代码块.最核心的目的是将可重复执行的操作进行封装,然后供调用方无限制的调用. JS中的函数的 ...

  9. 深入理解JavaScript中的函数操作——《JavaScript忍者秘籍》总结

    匿名函数 对于什么是匿名函数,这里就不做过多介绍了.我们需要知道的是,对于JavaScript而言,匿名函数是一个很重要且具有逻辑性的特性.通常,匿名函数的使用情况是:创建一个供以后使用的函数.简单的 ...

随机推荐

  1. php的stristr()函数,查找字符

    1.用法,要传2个参数 stristr(string,search):查找并返还匹配后,剩下的部分字符串 查找过程不区分大小写,要区分大小写用 strstr(string,search)少一个字母i ...

  2. 腾讯云AI平台张文杰:构建一站式机器学习服务平台

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 5月24日,以"无界数据无限智能"为主题的腾讯"云+未来"峰会AI大数据分论坛在广州拉开帷幕.此次分 ...

  3. HDU 5700——区间交——————【线段树+枚举】

    区间交 Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submiss ...

  4. SqlServer function 函数

    SqlServer的数据库Tsql还是很强大,以此来纪念下表值函数的语法吧. -- ============================================= -- Author: & ...

  5. javaEE Design Patter(2)详解3个设计模式

    Factory (bean creating) Proxy(Agent)(Aop)书有~ Templete(springMVC Facelets)//此处及以上忽略 工厂模式(Factory) 工厂模 ...

  6. How to limit Dialog's max height?

    1. We can make it to play trick in code. At Dialog's show function, after app has set contentView, w ...

  7. hdu 3535 (最少1,最多1,任意)(背包混合)(好题)

    http://blog.csdn.net/yan_____/article/details/8530833 这个问题一开始我用滚动,没有做出来,可能要需要先预处理排序才行,后来看了别人的方法,开始用二 ...

  8. java 用Graphics制作模糊验证码

    这篇随笔主要是java中制作验证码的效果,由于是在国庆前做的,现在也找不到原载了.我对自己整理的发表一份 生成的验证码效果如下: 一.建立一个工具类,用来生成验证码 package com.dkt.u ...

  9. js原生带缩略图的图片切换效果

    js原生带缩略图的图片切换效果 本例中用到的 moveElement(elementID,final_x,final_y,interval)是来自<JavaScript DOM编程艺术(中文第二 ...

  10. NodeJS require路径

    项目需要用nodejs,感觉nodejs是前端装逼神器了,是通向全栈工程师的必经之路哇,接下来开始踏上学习nodejs的征程.下面是第一个hello,world的程序. 1.server.js文件,这 ...