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