JavaScript 中Throttle节流函数如何使用
JavaScript 中Throttle节流函数如何使用,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
通常来说我们用下面的代码来实现这个功能:
var COUNT = 0; function testFn() { console.log(COUNT++); } // 浏览器resize的时候 // 1. 清除之前的计时器 // 2. 添加一个计时器让真正的函数testFn延后100毫秒触发 window.onresize = function () { var timer = null; clearTimeout(timer); timer = setTimeout(function() { testFn(); }, 100); };
细心的同学会发现上面的代码其实是错误的,这是新手会犯的一个问题:setTimeout 函数返回值应该保存在一个相对全局变量里面,否则每次 resize 的时候都会产生一个新的计时器,这样就达不到我们发的效果了
于是我们修改了代码:
var timer = null; window.onresize = function () { clearTimeout(timer); timer = setTimeout(function() { testFn(); }, 100); };
这时候代码就正常了,但是又多了一个新问题 —— 产生了一个全局变量 timer。这是我们不想见到的,如果这个页面还有别的功能也叫 timer 不同的代码之前就是产生冲突。为了解决这个问题我们要用 JavaScript 的一个语言特性:闭包 closures 。相关知识读者可以去 MDN 中了解,改造后的代码如下:
var throttle = function (fn, delay) { var timer = null; return function () { clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); } }; window.onresize = throttle(testFn, 200, 1000);
我们用一个闭包函数(throttle节流)把 timer 放在内部并且返回延时处理函数,这样以来 timer 变量对外是不可见的,但是内部延时函数触发时还可以访问到 timer 变量。
当然这种写法对于新手来说不好理解,我们可以变换一种写法来理解一下:
var throttle = function (fn, delay) { var timer = null; return function () { clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); } }; var f = throttle(testFn, 200); window.onresize = function () { f(); };
这里主要了解一点:throttle 被调用后返回的 function 才是真正的 onresize 触发时需要调用的函数
现在看起来这个方法已经接近***了,然而实际使用中并非如此。举个例子:
如果用户 不断的 resize 浏览器窗口大小,这时延迟处理函数一次都不会执行
于是我们又要添加一个功能:当用户触发 resize 的时候应该 在某段时间 内至少触发一次,既然是在某段时间内,那么这个判断条件就可以取当前的时间毫秒数,每次函数调用把当前的时间和上一次调用时间相减,然后判断差值如果大于 某段时间 就直接触发,否则还是走 timeout 的延迟逻辑。
下面的代码里面需要指出的是:
previous 变量的作用和 timer 类似,都是记录上一次的标识,必须是相对的全局变量
如果逻辑流程走的是“至少触发一次”的逻辑,那么函数调用完成需要把 previous 重置成当前时间,简单来说就是:相对于下一次的上一次其实就是当前
var throttle = function (fn, delay, atleast) { var timer = null; var previous = null; return function () { var now = +new Date(); if ( !previous ) previous = now; if ( now - previous > atleast ) { fn(); // 重置上一次开始时间为本次结束时间 previous = now; } else { clearTimeout(timer); timer = setTimeout(function() { fn(); }, delay); } } };
实践:
我们模拟一个窗口 scroll 时节流的场景,也就是说当用户滚动页面向下的时候我们需要节流执行一些方法,比如:计算 DOM 位置等需要连续操作 DOM 元素的动作
完整代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>throttle</title> </head> <body> <div style="height:5000px"> <div id="demo" style="position:fixed;"></div> </div> <script> var COUNT = 0, demo = document.getElementById('demo'); function testFn() {demo.innerHTML += 'testFN 被调用了 ' + ++COUNT + '次<br>';} var throttle = function (fn, delay, atleast) { var timer = null; var previous = null; return function () { var now = +new Date(); if ( !previous ) previous = now; if ( atleast && now - previous > atleast ) { fn(); // 重置上一次开始时间为本次结束时间 previous = now; clearTimeout(timer); } else { clearTimeout(timer); timer = setTimeout(function() { fn(); previous = null; }, delay); } } }; window.onscroll = throttle(testFn, 200); // window.onscroll = throttle(testFn, 500, 1000); </script> </body> </html>
我们用两个 case 来测试效果,分别是添加至少触发 atleast 参数和不添加:
// case 1 window.onscroll = throttle(testFn, 200); // case 2 window.onscroll = throttle(testFn, 200, 500);
case 1 的表现为:在页面滚动的过程(不能停止)中 testFN 不会被调用,直到停止的时候会调用一次,也就是说执行的是 throttle 里面 *** 一个 setTimeout ,效果如图(查看原 gif 图):
关于JavaScript 中Throttle节流函数如何使用问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网行业资讯频道了解更多相关知识。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341