為了完全理解這個老生常談的東西,查來查去,算是初步知道這是個什麼鬼,怎麼用,為什麼用 閉包: 外部函數定義的內部函數就是閉包。 閉包的作用及好處: 閉包給訪問外部函數定義的內部變數創造了條件。也將關於函數的一切封閉到了函數內部,減少了全局變數,這也是閉包的真實含義。 在理解閉包之前.最好能先理解一下 ...
為了完全理解這個老生常談的東西,查來查去,算是初步知道這是個什麼鬼,怎麼用,為什麼用 閉包: 外部函數定義的內部函數就是閉包。 閉包的作用及好處: 閉包給訪問外部函數定義的內部變數創造了條件。也將關於函數的一切封閉到了函數內部,減少了全局變數,這也是閉包的真實含義。 在理解閉包之前.最好能先理解一下作用域鏈的含義, 簡單來說,作用域鏈就是函數在定義的時候創建的,用於尋找使用到的變數的值的一個索引, 而他內部的規則是,把函數自身的本地變數放在最前面,把自身的父級函數中的變數放在其次,把再高一級函數中的變數放在更後面,以此類推直至全局對象為止. 當函數中需要查詢一個變數的值的時候,js解釋器會去作用域鏈去查找,從最前面的本地變數中先找,如果沒有找到對應的變數,則到下一級的鏈上找,一旦找到了變數,則不再繼續.如果找到最後也沒找到需要的變數,則解釋器返回undefined. 瞭解了作用域鏈, 我們再來看看js的記憶體回收機制, 一般來說,一個函數在執行開始的時候,會給其中定義的變數劃分記憶體空間保存,以備後面的語句所用,等到函數執行完畢返回了,這些變數就被認為是無用的了.對應的記憶體空間也就被回收了.下次再執行此函數的時候,所有的變數又回到最初的狀態,重新賦值使用. 但是如果這個函數內部又嵌套了另一個函數,而這個函數是有可能在外部被調用到的.並且這個內部函數又使用了外部函數的某些變數的話. 這種記憶體回收機制就會出現問題. 如果在外部函數返回後,又直接調用了內部函數,那麼內部函數就無法讀取到他所需要的外部函數中變數的值了. 所以js解釋器在遇到函數定義的時候,會自動把函數和他可能使用的變數(包括本地變數和父級和祖先級函數的變數(自由變數))一起保存起來.也就是構建一個閉包, 這些變數將不會被記憶體回收器所回收,只有當內部的函數不可能被調用以後(例如被刪除了,或者沒有了指針),才會銷毀這個閉包,而沒有任何一個閉包引用的變數才會被下一次記憶體回收啟動時所回收. 與普通函數的區別: 1,普通函數也能曝光內部的值。方法A定義全局變數,但占用的記憶體無法釋放且函數使用的變數定義到了函數外部不便於理解和管理。方法B將內部變數當參數傳遞,此種方法不美觀太醜陋。 2,函數每次執行時都會且只會初始化其內部變數,導致了閉包與普通函數的最大區別。就是每次調用普通函數時它內部都被初始化成一致狀態,導致執行的結果是一致的。閉包不同,它的本質是內部函數,調用閉包只會初始化內部函數變數,外部函數的變數沒有被初始化,實現了變數值的傳遞。外部函數只在定義閉包時被初始化。閉包消亡時記憶體被回收。 什麼時候需要使用閉包: 當每次調用函數A時都要改變全局變數B,且B只與A有關。以往沒有閉包時只能將B定義為全局變數,現在可以將B定義為A的內部變數,同時在A內部定義閉包C,並將C當值返回。 搞事情總得寫點代碼,那就寫寫吧:
1 function gg(){ 2 var b = 0; 3 return function(){ 4 b++; 5 return b 6 } 7 } 8 var tt = gg();
9 tt() //1 10 tt() //2 11 tt() //
閉包的特點很鮮明,閉包內,變數無法釋放,無法被直接訪問;閉包可以被延遲執行。所以可以用它來做一些事情: 管理私有變數和私有方法,將對變數(狀態)的變化封裝在安全的環境中 需要註意的,由於閉包內的部分資源無法自動釋放,容易造成記憶體泄露