<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## 什么是防抖和節流?有什么區別?如何實現? **防抖是控制次數,節流是控制頻率** 19回復 1. **防抖** > 觸發高頻事件后n秒內函數只會執行一次,如果n秒內高頻事件再次被觸發,則重新計算時間 * 思路: > 每次觸發事件時都取消之前的延時調用方法 ~~~js function debounce(fn) { let timeout = null; // 創建一個標記用來存放定時器的返回值 return function () { // 每當用戶輸入的時候把前一個 setTimeout clear 掉 clearTimeout(timeout); timeout = setTimeout(() => { // 然后又創建一個新的 setTimeout, //這樣就能保證輸入字符后的 interval 間隔內如果還有字符輸入的話, //就不會執行 fn 函數 fn.apply(this, arguments); //關鍵在第一個參數,為了確保上下文環境為當前的this,所以不能直接用fn。 }, 500); }; } function sayHi() { console.log('防抖成功'); } var inp = document.getElementById('inp'); inp.addEventListener('input', debounce(sayHi)); // 防抖 ~~~ 2. **節流** > 高頻事件觸發,但在n秒內只會執行一次,所以節流會稀釋函數的執行頻率 * 思路: > 每次觸發事件時都判斷當前是否有等待執行的延時函數 ~~~js function throttle(fn) { let canRun = true; // 通過閉包保存一個標記 return function () { if (!canRun) return; // 在函數開頭判斷標記是否為true,不為true則return canRun = false; // 立即設置為false setTimeout(() => { // 將外部傳入的函數的執行放在setTimeout中 fn.apply(this, arguments); //關鍵在第一個參數,為了確保上下文環境為當前的this,所以不能直接用fn。 // 最后在setTimeout執行完畢后再把標記設置為true //(關鍵)表示可以執行下一次循環了。 //當定時器沒有執行的時候標記永遠是false,在開頭被return掉 canRun = true; }, 500); }; } function sayHi(e) { console.log(e.target.innerWidth, e.target.innerHeight); } window.addEventListener('resize', throttle(sayHi)); ~~~ # start https://www.jianshu.com/p/c8b86b09daf0 在前端開發的過程中,我們經常會需要綁定一些持續觸發的事件,如 resize、scroll、mousemove 等等,但有些時候我們并不希望在事件持續觸發的過程中那么頻繁地去執行函數。 通常這種情況下我們怎么去解決的呢?一般來講,防抖和節流是比較好的解決方案。 讓我們先來看看在事件持續觸發的過程中頻繁執行函數是怎樣的一種情況。 html 文件中代碼如下 ~~~xml <div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div> <script> let num = 1; let content = document.getElementById('content'); function count() { content.innerHTML = num++; }; content.onmousemove = count; </script> ~~~ 在上述代碼中,div 元素綁定了 mousemove 事件,當鼠標在 div(灰色)區域中移動的時候會持續地去觸發該事件導致頻繁執行函數。效果如下 ![](//upload-images.jianshu.io/upload_images/4842858-652a8eb5c73db0c7.gif?imageMogr2/auto-orient/strip|imageView2/2/w/1132/format/webp) 可以看到,在沒有通過其它操作的情況下,函數被頻繁地執行導致頁面上數據變化特別快。所以,接下來讓我們來看看防抖和節流是如何去解決這個問題的。 ###### 防抖(debounce) **所謂防抖,就是指觸發事件后在 n 秒內函數只能執行一次,如果在 n 秒內又觸發了事件,則會重新計算函數執行時間。** 防抖函數分為非立即執行版和立即執行版。 非立即執行版: ~~~jsx function debounce(func, wait) { let timeout; return function () { let context = this; let args = arguments; if (timeout) clearTimeout(timeout); timeout = setTimeout(() => { func.apply(context, args) }, wait); } } ~~~ 非立即執行版的意思是觸發事件后函數不會立即執行,而是在 n 秒后執行,如果在 n 秒內又觸發了事件,則會重新計算函數執行時間。 我們依舊使用上述綁定 mousemove 事件的例子,通過上面的防抖函數,我們可以這么使用 ~~~swift content.onmousemove = debounce(count,1000); ~~~ 效果如下 ![](//upload-images.jianshu.io/upload_images/4842858-1f6389b9dd9e5ef9.gif?imageMogr2/auto-orient/strip|imageView2/2/w/1134/format/webp) 可以看到,在觸發事件后函數 1 秒后才執行,而如果我在觸發事件后的 1 秒內又觸發了事件,則會重新計算函數執行時間。 上述防抖函數的代碼還需要注意的是 this 和 參數的傳遞 ~~~jsx let context = this; let args = arguments; ~~~ 防抖函數的代碼使用這兩行代碼來獲取 this 和 參數,是為了讓 debounce 函數最終返回的函數 this 指向不變以及依舊能接受到 e 參數。 立即執行版: ~~~jsx function debounce(func,wait) { let timeout; return function () { let context = this; let args = arguments; if (timeout) clearTimeout(timeout); let callNow = !timeout; timeout = setTimeout(() => { timeout = null; }, wait) if (callNow) func.apply(context, args) } } ~~~ 立即執行版的意思是觸發事件后函數會立即執行,然后 n 秒內不觸發事件才能繼續執行函數的效果。 使用方法同上,效果如下 ![](//upload-images.jianshu.io/upload_images/4842858-067785c056f182d8.gif?imageMogr2/auto-orient/strip|imageView2/2/w/1140/format/webp) 在開發過程中,我們需要根據不同的場景來決定我們需要使用哪一個版本的防抖函數,一般來講上述的防抖函數都能滿足大部分的場景需求。但我們也可以將非立即執行版和立即執行版的防抖函數結合起來,實現最終的雙劍合璧版的防抖函數。 雙劍合璧版: ~~~jsx /** * @desc 函數防抖 * @param func 函數 * @param wait 延遲執行毫秒數 * @param immediate true 表立即執行,false 表非立即執行 */ function debounce(func,wait,immediate) { let timeout; return function () { let context = this; let args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { var callNow = !timeout; timeout = setTimeout(() => { timeout = null; }, wait) if (callNow) func.apply(context, args) } else { timeout = setTimeout(function(){ func.apply(context, args) }, wait); } } } ~~~ ###### 節流(throttle) **所謂節流,就是指連續觸發事件但是在 n 秒中只執行一次函數。**節流會稀釋函數的執行頻率。 對于節流,一般有兩種方式可以實現,分別是時間戳版和定時器版。 時間戳版: ~~~jsx function throttle(func, wait) { let previous = 0; return function() { let now = Date.now(); let context = this; let args = arguments; if (now - previous > wait) { func.apply(context, args); previous = now; } } } ~~~ 使用方式如下 ~~~swift content.onmousemove = throttle(count,1000); ~~~ 效果如下 ![](//upload-images.jianshu.io/upload_images/4842858-80423b8898a27732.gif?imageMogr2/auto-orient/strip|imageView2/2/w/1140/format/webp) 可以看到,在持續觸發事件的過程中,函數會立即執行,并且每 1s 執行一次。 定時器版: ~~~jsx function throttle(func, wait) { let timeout; return function() { let context = this; let args = arguments; if (!timeout) { timeout = setTimeout(() => { timeout = null; func.apply(context, args) }, wait) } } } ~~~ 使用方式同上,效果如下 ![](//upload-images.jianshu.io/upload_images/4842858-bf2ed4c8ed4f0ec0.gif?imageMogr2/auto-orient/strip|imageView2/2/w/1136/format/webp) 可以看到,在持續觸發事件的過程中,函數不會立即執行,并且每 1s 執行一次,在停止觸發事件后,函數還會再執行一次。 我們應該可以很容易的發現,其實時間戳版和定時器版的節流函數的區別就是,時間戳版的函數觸發是在時間段內開始的時候,而定時器版的函數觸發是在時間段內結束的時候。 同樣地,我們也可以將時間戳版和定時器版的節流函數結合起來,實現雙劍合璧版的節流函數。 雙劍合璧版: ~~~tsx /** * @desc 函數節流 * @param func 函數 * @param wait 延遲執行毫秒數 * @param type 1 表時間戳版,2 表定時器版 */ function throttle(func, wait ,type) { if(type===1){ let previous = 0; }else if(type===2){ let timeout; } return function() { let context = this; let args = arguments; if(type===1){ let now = Date.now(); if (now - previous > wait) { func.apply(context, args); previous = now; } }else if(type===2){ if (!timeout) { timeout = setTimeout(() => { timeout = null; func.apply(context, args) }, wait) } } } } ~~~ 作者:淘淘笙悅 鏈接:https://www.jianshu.com/p/c8b86b09daf0 來源:簡書 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。 ## 參考文章 * [第 3 題:什么是防抖和節流?有什么區別?如何實現?](https://www.muyiy.cn/question/js/3.html) * [JavaScript專題之跟著underscore學防抖](https://github.com/mqyqingfeng/Blog/issues/22) * [JavaScript專題之跟著 underscore 學節流](https://github.com/mqyqingfeng/Blog/issues/26)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看