假如技術HR問您JavaScript的「閉包」,嘿嘿嘿,舉這個例子就夠了

概念

當函數能夠記住並訪問所在的詞法做用域時,就產生了閉包,即便函數是在當前詞法做用域以外執行。前端

例子

問題

下面的代碼的運行結果和代碼語意上表達的不相符,咱們但願它可以每隔一秒輸出一次,每次輸出對應的數字,即第一秒後輸出1,第二秒後輸出2......
而這段代碼的運行結果是,第一秒後輸出6,第二秒後輸出6......
請解釋緣由而且提出修改方案。(包含要點,函數做用域,塊做用域,閉包,let)es6

for (var i = 1; i <= 5; i++) {
    setTimeout(function timer() {
        console.log(i);
    }, i*1000);
}

答案

解釋緣由

  1. var i,實際上聲明瞭一個全局變量segmentfault

  2. 延遲函數timer必然是在循環結束後纔開始執行,循環結束後,i=6前端框架

  3. 循環中確實定義了多個延遲函數timer,延遲函數在setTimeout的內部被回調,根據閉包概念,timer在其聲明以外的地方被調用,timer可以記住並訪問其聲明位置的詞法做用域,存在閉包閉包

  4. 實際上timer所記住的詞法做用域就是全局做用域,因此引用輸出的i都是6框架

修改方案

  1. 只要能保證每次循環都可以建立新的做用域,在新做用域中保存當前i的值便可函數

  2. 因此任何能夠建立新做用域的方法均可以達到效果,具體可參考這裏,經過分析這段代碼的進化歷程,或許可以加深您對JavaScript的做用域的理解code

常見的作法有ip

  1. 利用具名當即執行函數,每次循環都建立新做用域作用域

for (var i = 1; i <= 5; i++) {
    (function scope(j) {
        setTimeout(function timer() {
            console.log(j);
        }, i * 1000);
    })(i);
}
  1. 利用es6 let建立塊做用域

for (var i = 1; i <= 5; i++) {
    {
      let j = i;
      setTimeout(function timer() {
            console.log(j);
        }, i * 1000);
    }
}

總結

根據閉包的概念,只要有回調就會有閉包......
記住這個例子,還怕被問閉包?

後記

最近寫了三篇,參考書籍主要是《你不知道的JavaScript(上卷)》,看完這三篇能夠說就至關於看了半本書了,^_^前端框架層出不窮,不反對使用框架,但必定要有頭頭能駕馭的了它。試想,作前端的人那麼多,若是冰河世紀來臨,存活下來的仍是那些會寫框架的人,那些只懂框架使用的人就危險了我快畢業了,但內心慌慌的,因此若是您剛入門,建議仍是紮紮實實地學基礎,其實JS基礎遠比您想象的要複雜,千萬別覺得本身看了W3C School就以爲本身Ok了,反正我是要再努力下,爭取看得懂牛人的代碼哈下篇見......

相關文章
相關標籤/搜索