當函數能夠記住並訪問所在的詞法做用域時,就產生了閉包,即便函數是在當前詞法做用域以外執行。前端
下面的代碼的運行結果和代碼語意上表達的不相符,咱們但願它可以每隔一秒輸出一次,每次輸出對應的數字,即第一秒後輸出1,第二秒後輸出2......
而這段代碼的運行結果是,第一秒後輸出6,第二秒後輸出6......
請解釋緣由而且提出修改方案。(包含要點,函數做用域,塊做用域,閉包,let)es6
for (var i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i); }, i*1000); }
var i,實際上聲明瞭一個全局變量segmentfault
延遲函數timer必然是在循環結束後纔開始執行,循環結束後,i=6前端框架
循環中確實定義了多個延遲函數timer,延遲函數在setTimeout的內部被回調,根據閉包概念,timer在其聲明以外的地方被調用,timer可以記住並訪問其聲明位置的詞法做用域,存在閉包閉包
實際上timer所記住的詞法做用域就是全局做用域,因此引用輸出的i都是6框架
只要能保證每次循環都可以建立新的做用域,在新做用域中保存當前i的值便可函數
因此任何能夠建立新做用域的方法均可以達到效果,具體可參考這裏,經過分析這段代碼的進化歷程,或許可以加深您對JavaScript的做用域的理解code
常見的作法有ip
利用具名當即執行函數,每次循環都建立新做用域作用域
for (var i = 1; i <= 5; i++) { (function scope(j) { setTimeout(function timer() { console.log(j); }, i * 1000); })(i); }
利用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了,反正我是要再努力下,爭取看得懂牛人的代碼哈下篇見......