最新公告  | 
  • CTRL + D 加入收藏不迷路哦

  • 欢迎您光临码云笔记网,一个关注WEB前端开发的个人技术博客!

第7题:节流函数的作用是什么?应用场景有哪些 如何实现

节流概念

节流跟防抖,它们既有相似之处但又有所不同,很容易混淆。这里通过两个比喻来加深理解,先来说说节流。

节流的概念可以想象一下水坝,你建了水坝在河道中,不能让水流动不了,你只能让水流慢些。换言之,你不能让用户的方法都不执行。(个人比较喜欢这个比喻,因为它很形象的说出了跟防抖的区别。)

节流函数的作用

节流函数的作用是规定一个单位时间,在这个单位时间内最多只能触发一次函数执行,如果这个单位时间内多次触发函数,只能有一次生效。

举例说明:小明的妈妈和小明约定好,如果小明在周考中取得满分,那么当月可以带他去游乐场玩,但是一个月最多只能去一次。

这其实就是一个节流的例子,在一个月的时间内,去游乐场最多只能触发一次。即使这个时间周期内,小明取得多次满分。

节流应用场景

1.按钮点击事件

2.拖拽事件

3.onScoll

4.计算鼠标移动的距离(mousemove)

节流函数实现

利用时间戳实现

<html>
 <head></head>
 <body>
  <span class="token keyword">function</span> 
  <span class="token function">throttle</span> 
  <span class="token punctuation">(</span>func
  <span class="token punctuation">,</span> delay
  <span class="token punctuation">)</span> 
  <span class="token punctuation">{</span> 
  <span class="token keyword">var</span> lastTime 
  <span class="token operator">=</span> 
  <span class="token number">0</span>
  <span class="token punctuation">;</span> 
  <span class="token keyword">function</span> 
  <span class="token function">throttled</span>
  <span class="token punctuation">(</span>
  <span class="token punctuation">)</span> 
  <span class="token punctuation">{</span> 
  <span class="token keyword">var</span> context 
  <span class="token operator">=</span> 
  <span class="token keyword">this</span>
  <span class="token punctuation">;</span> 
  <span class="token keyword">var</span> args 
  <span class="token operator">=</span> arguments
  <span class="token punctuation">;</span> 
  <span class="token keyword">var</span> nowTime 
  <span class="token operator">=</span> Date
  <span class="token punctuation">.</span>
  <span class="token function">now</span>
  <span class="token punctuation">(</span>
  <span class="token punctuation">)</span>
  <span class="token punctuation">;</span> 
  <span class="token keyword">if</span>
  <span class="token punctuation">(</span>nowTime 
  <span class="token operator">&gt;</span> lastTime 
  <span class="token operator">+</span> delay
  <span class="token punctuation">)</span> 
  <span class="token punctuation">{</span> func
  <span class="token punctuation">.</span>
  <span class="token function">apply</span>
  <span class="token punctuation">(</span>context
  <span class="token punctuation">,</span> args
  <span class="token punctuation">)</span>
  <span class="token punctuation">;</span> lastTime 
  <span class="token operator">=</span> nowTime
  <span class="token punctuation">;</span> 
  <span class="token punctuation">}</span> 
  <span class="token punctuation">}</span> 
  <span class="token comment">//防抖函数最终返回的是一个函数</span> 
  <span class="token keyword">return</span> throttled
  <span class="token punctuation">;</span> 
  <span class="token punctuation">}</span>
 </body>
</html>

利用定时器实现

<html>
 <head></head>
 <body>
  <span class="token keyword">function</span> 
  <span class="token function">throttle</span>
  <span class="token punctuation">(</span>func
  <span class="token punctuation">,</span> delay
  <span class="token punctuation">)</span> 
  <span class="token punctuation">{</span> 
  <span class="token keyword">var</span> timeout 
  <span class="token operator">=</span> 
  <span class="token keyword">null</span>
  <span class="token punctuation">;</span> 
  <span class="token keyword">function</span> 
  <span class="token function">throttled</span>
  <span class="token punctuation">(</span>
  <span class="token punctuation">)</span> 
  <span class="token punctuation">{</span> 
  <span class="token keyword">var</span> context 
  <span class="token operator">=</span> 
  <span class="token keyword">this</span>
  <span class="token punctuation">;</span> 
  <span class="token keyword">var</span> args 
  <span class="token operator">=</span> arguments
  <span class="token punctuation">;</span> 
  <span class="token keyword">if</span>
  <span class="token punctuation">(</span>
  <span class="token operator">!</span>timeout
  <span class="token punctuation">)</span> 
  <span class="token punctuation">{</span> timeout 
  <span class="token operator">=</span> 
  <span class="token function">setTimeout</span>
  <span class="token punctuation">(</span>
  <span class="token punctuation">(</span>
  <span class="token punctuation">)</span>
  <span class="token operator">=&gt;</span>
  <span class="token punctuation">{</span> func
  <span class="token punctuation">.</span>
  <span class="token function">apply</span>
  <span class="token punctuation">(</span>context
  <span class="token punctuation">,</span> args
  <span class="token punctuation">)</span>
  <span class="token punctuation">;</span> 
  <span class="token function">clearTimeout</span>
  <span class="token punctuation">(</span>timeout
  <span class="token punctuation">)</span>
  <span class="token punctuation">;</span> timeout
  <span class="token operator">=</span>
  <span class="token keyword">null</span> 
  <span class="token punctuation">}</span>
  <span class="token punctuation">,</span> delay
  <span class="token punctuation">)</span>
  <span class="token punctuation">;</span> 
  <span class="token punctuation">}</span> 
  <span class="token punctuation">}</span> 
  <span class="token keyword">return</span> throttled
  <span class="token punctuation">;</span> 
  <span class="token punctuation">}</span>
 </body>
</html>

时间戳和定时器的方式都没有考虑最后一次执行的问题,比如有个按钮点击事件,设置的间隔时间是1S,在第0.5S,1.8S,2.2S点击,那么只有0.5S和1.8S的两次点击能够触发函数执行,而最后一次的2.2S会被忽略。

组合实现,允许设置第一次或者最后一次是否触发函数执行

function throttle (func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if (!options) options = {};

    var later = function () {
        previous = options.leading === false ? 0 : Date.now() || new Date().getTime();
        timeout = null;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
    };

    var throttled = function () {
        var now = Date.now() || new Date().getTime();
        if (!previous && options.leading === false) previous = now;
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
        } else if (!timeout && options.trailing !== false) {
            // 判断是否设置了定时器和 trailing
            timeout = setTimeout(later, remaining);
        }
        return result;
    };

    throttled.cancel = function () {
        clearTimeout(timeout);
        previous = 0;
        timeout = context = args = null;
    };

    return throttled;
}

使用很简单:

btn.onclick = throttle(handle, 1000, {leading:true, trailing: true});

以判断页面是否滚动到底部为例,普通的做法就是监听 window 对象的 scroll 事件,然后再函数体中写入判断是否滚动到底部的逻辑:

<html>
 <head></head>
 <body>
  <span class="pl-en">$</span>(
  <span class="pl-c1">window</span>).
  <span class="pl-en">on</span>(
  <span class="pl-s"><span class="pl-pds">'</span>scroll<span class="pl-pds">'</span></span>, 
  <span class="pl-k">function</span>() { 
  <span class="pl-c">// 判断是否滚动到底部的逻辑</span> 
  <span class="pl-k">let</span> pageHeight 
  <span class="pl-k">=</span> 
  <span class="pl-en">$</span>(
  <span class="pl-s"><span class="pl-pds">'</span>body<span class="pl-pds">'</span></span>).
  <span class="pl-c1">height</span>(), scrollTop 
  <span class="pl-k">=</span> 
  <span class="pl-en">$</span>(
  <span class="pl-c1">window</span>).
  <span class="pl-en">scrollTop</span>(), winHeight 
  <span class="pl-k">=</span> 
  <span class="pl-en">$</span>(
  <span class="pl-c1">window</span>).
  <span class="pl-c1">height</span>(), thresold 
  <span class="pl-k">=</span> pageHeight 
  <span class="pl-k">-</span> scrollTop 
  <span class="pl-k">-</span> winHeight; 
  <span class="pl-k">if</span> (thresold 
  <span class="pl-k">&gt;</span> 
  <span class="pl-k">-</span>
  <span class="pl-c1">100</span> 
  <span class="pl-k">&amp;&amp;</span> thresold 
  <span class="pl-k">&lt;=</span> 
  <span class="pl-c1">20</span>) { 
  <span class="pl-en">console</span>.
  <span class="pl-c1">log</span>(
  <span class="pl-s"><span class="pl-pds">'</span>end<span class="pl-pds">'</span></span>); } });
 </body>
</html>

如下图:

 节流(throttle)函数的作用是什么

这样做的一个缺点就是比较消耗性能,因为当在滚动的时候,浏览器会无时不刻地在计算判断是否滚动到底部的逻辑,而在实际的场景中是不需要这么做的,在实际场景中可能是这样的:在滚动过程中,每隔一段时间在去计算这个判断逻辑。而函数节流所做的工作就是每隔一段时间去执行一次原本需要无时不刻地在执行的函数,所以在滚动事件中引入函数的节流是一个非常好的实践:

<html>
 <head></head>
 <body>
  <span class="pl-c">// 添加节流函数</span> 
  <span class="pl-k">function</span> 
  <span class="pl-en">throttle</span>(
  <span class="pl-smi">fn</span>, 
  <span class="pl-smi">interval</span> 
  <span class="pl-k">=</span> 
  <span class="pl-c1">300</span>) { 
  <span class="pl-k">let</span> canRun 
  <span class="pl-k">=</span> 
  <span class="pl-c1">true</span>; 
  <span class="pl-k">return</span> 
  <span class="pl-k">function</span>() { 
  <span class="pl-k">if</span> (
  <span class="pl-k">!</span>canRun) 
  <span class="pl-k">return</span>; canRun 
  <span class="pl-k">=</span> 
  <span class="pl-c1">false</span>; 
  <span class="pl-c1">setTimeout</span>(() 
  <span class="pl-k">=&gt;</span> { 
  <span class="pl-smi">fn</span>.
  <span class="pl-c1">apply</span>(
  <span class="pl-c1">this</span>, 
  <span class="pl-c1">arguments</span>); canRun 
  <span class="pl-k">=</span> 
  <span class="pl-c1">true</span>; }, interval); }; } 
  <span class="pl-en">$</span>(
  <span class="pl-c1">window</span>).
  <span class="pl-en">on</span>( 
  <span class="pl-s"><span class="pl-pds">'</span>scroll<span class="pl-pds">'</span></span>, 
  <span class="pl-en">throttle</span>(
  <span class="pl-k">function</span>() { 
  <span class="pl-c">// 判断是否滚动到底部的逻辑</span> 
  <span class="pl-k">let</span> pageHeight 
  <span class="pl-k">=</span> 
  <span class="pl-en">$</span>(
  <span class="pl-s"><span class="pl-pds">'</span>body<span class="pl-pds">'</span></span>).
  <span class="pl-c1">height</span>(), scrollTop 
  <span class="pl-k">=</span> 
  <span class="pl-en">$</span>(
  <span class="pl-c1">window</span>).
  <span class="pl-en">scrollTop</span>(), winHeight 
  <span class="pl-k">=</span> 
  <span class="pl-en">$</span>(
  <span class="pl-c1">window</span>).
  <span class="pl-c1">height</span>(), thresold 
  <span class="pl-k">=</span> pageHeight 
  <span class="pl-k">-</span> scrollTop 
  <span class="pl-k">-</span> winHeight; 
  <span class="pl-k">if</span> (thresold 
  <span class="pl-k">&gt;</span> 
  <span class="pl-k">-</span>
  <span class="pl-c1">100</span> 
  <span class="pl-k">&amp;&amp;</span> thresold 
  <span class="pl-k">&lt;=</span> 
  <span class="pl-c1">20</span>) { 
  <span class="pl-en">console</span>.
  <span class="pl-c1">log</span>(
  <span class="pl-s"><span class="pl-pds">'</span>end<span class="pl-pds">'</span></span>); } }) );
 </body>
</html>

如图:

函数节流

加上函数节流之后,当页面再滚动的时候,每隔300ms才会去执行一次判断逻辑。

简单来说,函数的节流就是通过闭包保存一个标记(canRun=true),在函数的开头判断这个标记是否为true,如果为true的话就继续执行函数,否则则return掉,判断完标记后立即把这个标记设为false,然后把外部传入的函数的执行包在一个setTimeout中,最后在setTimeout执行完毕后再把标记设置为true(这里很关键),表示可以执行下一次的循环了。当setTimeout还未执行的时候,canRun这个标记始终为false,在开头的判断中被return掉。

防抖和节流都是为了避免事件的频繁触发,有些事件无论是防抖还是节流都做到性能上的优化,要根据业务需求选择合适的方案。

具体参考我之前写的文章:《第6题:什么是防抖和节流 有什么区别 如何实现

结语

我的回答如有不对的地方,麻烦务必指出来,我及时改正,以免误导别人,让我们共同进步吧!

1. 本站所有免费资源来源于用户上传和网络,因此不包含技术服务请大家谅解!如有侵权请邮件联系客服!
2. 本站不保证所提供下载的免费资源的准确性、安全性和完整性,免费资源仅供下载学习之用!如有链接无法下载、失效,请联系客服处理!
3. 您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源!如用于商业或者非法用途,与本站无关,一切后果请用户自负!
4. 如果您也有好的资源或技术教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
5. 加入前端开发QQ群:565733884,我们大家一起来交流技术!
码云笔记 » 第7题:节流函数的作用是什么?应用场景有哪些 如何实现

发表评论