基于 《你不知道的JavaScript上卷》 談談自己對閉包的理解。
首先明確下閉包的定義, 當函數(shù)可以記住并訪問所在的詞法作用域,就產(chǎn)生了閉包,即使函數(shù)是在當前詞法作用域之外執(zhí)行(關于作用域和詞法作用域,大家可以搜搜其他文章,這里就不做多解釋啦);
可以將閉包定義總結成三點:?
? ? ? ? ? ? 1.一定是函數(shù)對象
? ? ? ? ? ? 2.函數(shù)能夠在其詞法作用域外 被訪問 (這個外,包含 空間和時間)
? ? ? ? ? ? 3.使得作用域的生命周期得以延續(xù)。(第二點和第三點相輔相成)
還有一句比較重要的概念,需要謹記:
? ? ? ? ? ? 在JavaScript中函數(shù)是運行在被定義時的作用域中的,而不是運行環(huán)境的作用域
下面我們看段代碼,理解下閉包:


先看圖1,foo()?函數(shù)中定義了 bar()?函數(shù),因此bar()?的詞法作用域可以訪問foo()?的內部作用域。
根據(jù)閉包的定義,圖一的函數(shù)并不是閉包,因為bar封閉在了foo()?內部,函數(shù)不能在定義時的詞法作用域外被訪問。
再看圖2,函數(shù)bar()?是被定義在foo() 函數(shù)作用域中,同樣bar()?的詞法作用域可以訪問foo()?的內部作用域,foo()?函數(shù)返回bar()?; foo()?函數(shù)執(zhí)行后的返回值 即 bar()?函數(shù)的對象,之后賦值給了baz變量。調用baz()?,實際上調用內部函數(shù)bar()?;
根據(jù)我們總結得閉包三點定義,我們再來看這個函數(shù),首先bar()? 是個函數(shù),滿足第一點,是函數(shù)對象。?
其次,baz()?的調用相當于在 全局作用域中調用了foo()?作用域中的bar()?函數(shù),滿足第二點,函數(shù)能后再其詞法作用域外被訪問。(這里的外指的空間之外)
最后,通常來說,foo()?函數(shù)運行完之后,整個內部作用域會被垃圾回收清理掉,但是由于bar()?的存在,為了供bar在以后時間被調用,bar()?保持對foo整個內部作用域的引用,因此foo()?無法被垃圾回收,換句話說,就是foo的內部作用域生命周期得以延續(xù)。
為了加深理解,我們再看一段代碼:

wait() 函數(shù)內部 定義了 setTimeout() 函數(shù),setTimeout()函數(shù) 有回調韓式timer() 。
timer() 函數(shù)就是閉包,可能某些人不能理解,我們在用閉包定義來分析,
首先 timer() 是個函數(shù),滿足上文總結得閉包定義第一點。
其次 timer()?函數(shù)是在 wait()執(zhí)行后 1000ms 被調用, 滿足第二點要求 。 timer() 所在的詞法作用域是wait函數(shù)的內部作用域。wait()在執(zhí)行1000ms后,timer()仍保留對wait()作用域的訪問。滿足了 函數(shù)能夠在其詞法作用域之外被訪問,而此時的外 指的就是時間之外。
最后,wait()作用域由于 timer() 函數(shù),生命周期得以延續(xù),滿足第三點。
所以,timer() 是個閉包。
以上是個人的一些理解,如果不同意見,歡迎討論。