完全搞懂JavaScript的閉包、防抖跟節流

最近出去面試了一下,收穫頗多!!!

之前的我,追求實際,比較追求實用價值,然而最近面試,傳說中的面試造火箭,工做擰螺絲,居然被我遇到了。雖然不少知識點在實際工做中並不常常用到,但人家就是靠這個來篩選人才的!!!前端

在大學裏,你可能會感受到微積分沒啥用處,去菜市場買菜,你不可能用到微積分吧,可是呢,這個知識卻決定了你能在哪一個菜市場買菜。請細品!面試

關於前端方面,我用的是jQuery,前端採用Ajax請求數據,後端返回JSON數據,獲得數據後,再經過jQuery去操做DOM。後端

這裏可能有個誤解,不少人會覺得jQuery是一種框架,然而現實並不是如此,它只是個工具庫而已,簡化了JavaScript的一些語法。可能不少人用習慣了jQuery後,甚至都分不清jQuery語法跟JavaScript語法。jQuery能作的事情,JavaScript也能作,只是簡單地簡化了一些寫法。緩存

閒話少說,開始正文。安全

1、談談對閉包的理解

從概念上來說,所謂的閉包指的是一個函數,一個什麼樣的函數呢?有權訪問另外一個函數做用域中的變量的函數。
直觀點來說,JavaScript的閉包就是函數中嵌套函數。
本質上來說,是由於做用域而產生一種特殊的狀況,從而致使函數內部的變量沒法進行銷燬。若是再去深究的話,會出現個做用域鏈,這裏面比較難理解。也是閉包產生的根本緣由。
舉個例子:服務器

function myMethod(){
      var num=6;
      return function(){
            var n=0;
            console.log(++n);
            console.log(++num);
      }
}
myMethod();//執行後,返回的是個方法(就是所謂的閉包),由於這個方法中還用到了myMethod裏面的變量num,會致使num沒法釋放。

//能夠證實
var myFn=myMethod();//將返回的方法用變量保存起來
myFn();//此時的n=1,num=7    num沒有被銷燬,仍然保存着值。這即是閉包的精髓所在,有點像C#/JAVA裏面的靜態變量
myFn();//此時的n=1,num=8     

myMethod()();//若是這樣運行的話,輸出永遠是n=1,num=7    跟上面是有區別的,上面是用了個變量將其保存了起來。

閉包的優缺點以下:
優勢:一、保護函數內部的變量安全,實現封裝
二、能夠把這個變量看成緩存來使用
弊端:沒法銷燬變量,使用不當,容易形成內存泄漏閉包

你可能會問,閉包的使用場景究竟是什麼呢?
這個問題可能令你非常疑惑,我也想了好久。直到最近的一次面試,才豁然開朗。接下來看看js的防抖跟節流。框架

2、談談對防抖的理解

防抖,從字面上來理解,即是防止抖動。
這個,咱們應該會常常接觸到,可能本身不知道而已。咱們常用百度吧,沒事百度搜索一下。在這裏,當咱們輸入關鍵詞的時候,會出現一些聯想詞,供咱們去選擇。這裏面便用到了防抖。

試想,若是是咱們,怎麼去實現這個功能,正常狀況下,咱們確定會這樣寫函數

<input type="text" id="txt_test" />
//相應的js代碼
var txt_test=document.getElementById("txt_test");
txt_test.oninput=function(){
    console.log(txt_test.value);
    //其餘代碼
    請求後端數據
}

對應的結果

這樣寫,的確不錯。每輸入一個字符,觸發一次輸入事件。工具

聯想到實際
現實中使用百度的人成千上萬,那每次輸入一個字符,請求後端一次,這麼多人的話同時使用百度的話,那請求也太多了,確定會致使服務器壓力賊大,咱們知道,這些請求中不少是沒有什麼實際意義的,基於這種場景,怎麼優化呢?

試想一下,現實中,咱們是怎麼搜索的?
當咱們搜索ghost的時候,應該是快速地輸入,不可能一個字符一個字符的輸入的吧,那打字速度也忒慢了吧。當輸入完的時候,確定會停頓一下,而這時候再去觸發一次輸入事件豈不正好?此時,防抖,應運而生。

function debounce(fn,wait) {
    var timeout = null; // 建立一個標記用來存放定時器的返回值
    return function () {      
        clearTimeout(timeout); //清除定時器
        //建立新的 setTimeout
        timeout = setTimeout(function(){
            fn();
        }, wait);
    };
}
// 處理函數
function handle() {
    console.log(document.getElementById("kw").value);
}
        
//輸入框事件
document.getElementById("kw").addEventListener('input', debounce(handle,5000));

以上的代碼即是百度搜索中,無論怎麼輸入,只有在輸入完成後,停頓5s(這個時間故意設置這麼長的),纔會觸發一次handle方法。

3、談談對節流的理解

咱們應該也會常常接觸到,好比打開一個網頁,往下滾動的時候,會出現滾動條,當滾動到必定的程度時,會出現一個返回頂部的按鈕,點擊一下,便會返回到頂部。在這裏,便會用到節流。

試想,若是是咱們,須要實現以上的功能,咱們是如何寫代碼的。
首先,先寫一個div經過position:fixed;display:none;固定到頁面右下角。而後寫一個滾動事件,

window.onscroll=function(e){
      //相應的處理代碼
      handler();距離多少的位置,顯示按鈕或者隱藏按鈕。
}
function handler(){
      console.log("滾動事件觸發");
}

在這裏,當咱們向下滾動頁面的時候,你會發現,這個onscroll事件觸發的頻率過高過高,稍微向下滾動一丟丟,就已經觸發了不少次,並且其中不少的觸發並無實在的意義,如何減小觸發的頻率,減小那麼多的計算操做呢?

現實中,屏幕刷新率通常在60HZ,咱們看到的靜態畫面並非徹底不動的,只是動的太快,人眼沒法覺察出,誤覺得是不動的。若是過慢的話,咱們天然會看得出來,過快的話,沒啥實際意義,刷新的過快了,經過人眼,咱們仍是認爲屏幕是不動的。
既然這樣,那可不能夠這樣?
在人眼識別的範圍內,間接地去觸發這個事件。豈不正好?這時候,節流便誕生了。

所謂的節流呢,其思想指的就是某些代碼不能夠在沒有間斷的狀況下連續重複執行。相似的還有onresize等事件。

function throttle(fn,delay){
      var canRun=true;//經過閉包保存該變量
      return function(){
            if(!canRun) return;//馬上返回
            canRun=false;
            setTimeout(function(){
                  fn();
                  canRun=true;
            },delay);
      };
}

function handle(){
      console.log(123);
}

window.addEventListener("scroll",throttle(handle,2000));

以上的代碼即是觸發窗口滾動事件後,每2s執行一次handle方法。

總結

以上的防抖跟節流的方法,都是比較基礎的方法。至於怎麼升級,就靠我的了
防抖跟節流都用到了閉包,
使用的都是計時器setTimeout,
防抖是某個事件觸發後幾秒後執行相應的方法,
而節流是某個事件觸發後周期性執行相應的方法。

相關文章
相關標籤/搜索