JavaScript 閉包
先來看一下各個不同地方的閉包說法
- 你不知道的JavaScript: 當函數可以記住並訪問所在的詞法作用域時及形成閉包,即使不是在該函數的詞法作用域內
- JavaScript高级程序设计: 閉包是指有權訪問令一個函數作用域變量的函數
- MDN: 閉包(Closure)是函式以及該函式被宣告時所在的作用域環境(lexical environment)的組合。
我自己的理解
如果我們把函數作用域想像成一個書包(從外面是看不到裡面的內容的),那閉包就很像一個超一流扒手,可以偷窺到你書包的東西。
先來個情境
聲明一個背包函數
1 | function package() { |
外界的人看起來會長怎樣(那整個黃黃的一塊講的艱澀一點就是package函數的詞法作用域)
假設今天有一個扒手函數可以調用到鉛筆盒那就形成閉包(如同先前定義的)
附註: 這裡我對package裡面改寫成這樣:
1 | function package() { |
之後扒手開扒
1 | let pickpocket = package() // 通常函數執行完該作用域的東西就該銷毀了 |
可以發現確實成功獲得書包裡面的鉛筆盒!!!!
其實這跟棧內存有關
擷取
棧內存銷毀:
- 全局棧內存:關掉頁面的時候才會銷毀
- 私有棧內存:
- 1.一般情況下,函數只要執行完成,形成的私有棧內存就會被銷毀釋放掉(排除出現無限極遞歸、出現死循環的模式)
- 2.但是一旦棧內存中的某個東西(一般都是堆地址)被私有作用域以外的事物給佔用了,則當前私有棧內存不能立即被釋放銷毀(特點:私有作用域中的私有變量等信息也保留下來了=>這種函數執行形成不能被釋放的私有棧內存,也叫做閉包)
從上面兩個可以得出函數作用域裡面確實不會被立即銷毀
到此我們可以知道
只要外面有任何事物佔據了私有作用域裡面的東西就會產生閉包(保存該函數作用域)
所以其實我們要產生閉包的核心觀念是要讓外界竊聽到函數內部,這才是為何我們在函數內要return函數的原因(讓外界卡住內存)的原因
所以下面講幾個例子吧!
例子
範例一 最基本閉包
1 | function foo(num) { |
改一下範例一看會長怎樣
1 | function foo(num) { |
可以發現確實可以訪問到num,且我們這邊可以發現會產生不同的值,代表這兩個都會儲存內存,因此我們要小心使用,不然可能會一直浪費內存
還可以這樣
1 | function foo(num) { |
或是這樣
1 | function foo(a) { |