詳解JS閉包概念

閉包理解

1. 如何產生閉包?

* 當一個嵌套的內部 ( 子 ) 函數引用了嵌套的外部 ( 父 ) 函數的變量 ( 函數 ) 時,產生閉包

2. 閉包到底是什麼?

* 使用 Chrome 調試查看

* 理解一:閉包是嵌套的內部函數 ( 絕大部分人 )

* 理解二:包含被引用變量 ( 函數 ) 的對象 ( 極少數人 )

* 注意:閉包存在於嵌套的內部函數中

3. 產生閉包的條件?

* 函數嵌套

* 內部函數引用了外部函數的數據 ( 變量 / 函數 )

* 執行函數定義 ( 比如執行外部函數時 )

(執行函數定義就會產生閉包,不用調用內部函數,調用外部函數時會預處理產 生執行上下文,那時就可以執行內部函數的定義了)

這種函數定義,在22行就已經產生閉包。

詳解JS閉包概念

如果把函數定義賦給變量這種形式,在22行處就不能產生閉包,因為預處理時會把fn2當做變量提升,賦值undefined,也沒有執行函數定義。也就無法產生閉包。

這種方式聲明的函數對象,要在 26 行整個函數定義執行完才會產生閉包。

常見的閉包

1. 將函數作為另一個函數的返回值

2. 將函數作為實參傳遞給另一個函數調用

看內部函數創建幾次,就看外部函數執行幾次。

反覆執行內部函數的過程中,閉包為什麼沒有消失?

詳解JS閉包概念

如果沒有閉包,在執行完 var f =fn1() 後 a 就消失了,後面的 f() 沒法從 fn1 裡面調 a 。

詳解JS閉包概念

閉包裡面有 msg ,沒有 time

閉包的作用

1. 使用函數內部的變量在函數執行完後,仍然存活在內存中 ( 延長了局部變量的生 命週期 )

2. 讓函數外部可以操作 ( 讀寫 ) 到函數內部的數據 ( 變量 / 函數 )

問題:

1. 函數執行完後,函數內部聲明的局部變量是否還存在?

一般不存在,存在於閉包中的變量才可能存在。

( “可能”是指:閉包所在的函數對象不成為垃圾對象 )

按理說,函數執行完後其內部的屬性都會被釋放,但如果該函數的某個內部函數 對象通過在外部函數內 return 的方式賦給某個全局變量,

那麼這個函數對象就存 活下來了,而和它有關聯的閉包裡的局部變量也會存活。

2. 在函數外部能直接訪問函數內部的局部變量嗎?

不能。但我們可以通過閉包來讓外部操作它。

( “通過閉包來讓外部操作”是指:函數嵌套一個內部函數,在這個內部函數對 象裡寫類似 a++ 這種可以修改函數內部變量的操作,

然後把這個內部函數對象賦給 外部的全局變量,就可以在外部操作函數內部的局部變量)

詳解JS閉包概念

閉包的生命週期

1. 產生:在嵌套內部函數定義執行完時就產生了(不是在調用)

2. 死亡:在嵌套的內部函數成為垃圾對象時

閉包死亡(包含閉包的函數對象成為垃圾對象)

f = null

閉包應用 _ 自定義 JS 模塊

* 具有特定功能的 js 文件

* 將所有的數據和功能都封裝在一個函數內部(私有的)

* 只向外暴露一個包含 n 個方法的對象或方法

* 模塊的使用者,只需要通過模塊暴露的對象調用方法來實現對應的功能

第一種

詳解JS閉包概念

詳解JS閉包概念

第二種

詳解JS閉包概念

詳解JS閉包概念

這種直接就可以調用,比第一種更方便。

另外,壓縮代碼時的細節,在頂部和底部的小括號寫成 window 。

就可以將所有 window 壓縮成 w 。

閉包的缺點及解決

1. 缺點

* 函數執行完後,函數內的局部變量沒有釋放,佔用內存時間會變長

* 容易造成內存洩露

2. 解決

* 能不用閉包就不用

* 及時釋放

f = null // 讓內部函數成為垃圾對象 --> 回收閉包

內存溢出與內存洩露

1. 內存溢出

* 一種程序運行出現的錯誤

* 當程序運行需要的內存超過剩餘的內存時,就拋出內存溢出的錯誤

2. 內存洩露

* 佔用的內存沒有及時釋放

* 內存洩露積累過多了就導致內存溢出

* 常用的內存洩露:

* 意外的全局變量(函數內不加 var 定義的變量)

* 沒有及時清理的定時器或回調函數(啟動循環定時器後不清理)

* 閉包(沒有主動 f=null 讓內嵌的函數對象成為垃圾對象,閉包也會一直存在)


分享到:


相關文章: