本文一共 1300 字,讀完只需 5 分鐘javascript
閉包
, 能夠說是每一個前端工程師都據說的一個詞,咋一看很難從字面上去理解,從而給人留下了閉包
是一個重要又難以理解的概念。前端
可是,閉包在 JS 代碼能夠說是隨處可見,閉包也只是計算機領域的一個概念而已,它的存在是由於 JS 的一些語言特性,好比:函數式語言
,執行上下文
,執行上下文棧
,做用域鏈
,詞法做用域
。java
執行上下文:Execution Context
執行上下文棧:Execution Context Stack
做用域鏈:Scope Chain
做用域:Scopebash
本篇文章,將先給結論,到底什麼是閉包,再來分析產生閉包的過程和緣由。前端工程師
當函數記住並訪問所在詞法做用域的自由變量時,就產生了閉包,即便函數是在當前詞法做用域外執行。 --《你不知道的 JavaScript》閉包
來段經典的閉包代碼:app
function outter() {
var a = 123;
function inner() {
console.log(a);
}
return inner;
}
var foo = outter();
foo(); // 123
複製代碼
內部函數 inner 記住了它被定義時的詞法做用域,也就是 outter 的函數做用域,並訪問了該做用域裏的自由變量 a, 同時,inner 函數做用返回值,在外部做用域中被執行。函數
以上描述,所有符合閉包的描述,那這就是閉包
。post
以前的文章講了函數的執行上下文棧,變量對象,做用域鏈等內容,接下來經過閉包代碼回顧代碼是怎麼樣的執行過程。ui
function outter() {
var a = 123;
function inner() {
console.log(a);
}
return inner;
}
var foo = outter();
foo(); // 123
複製代碼
ECStack = [
globalContext
];
複製代碼
globalContext = {
VO: global,
scope: [global.VO],
this: global
}
複製代碼
outter.[[scope]] = [
globalContext.VO
];
複製代碼
ECStack = [
globalContext,
outterContext
];
複製代碼
outterContext = {
AO: {
arguments: {
a: undefined,
}
length: 1
},
scope: undefined,
inner: reference to function inner(){}
Scope: [AO, globalContext.VO],
this: undefined
}
複製代碼
ECStack = [
globalContext,
innerContext
];
複製代碼
innerContext = {
AO: {
arguments: {
length: 0
}
},
Scope: [AO, outterContext.AO, globalContext.VO],
this: undefined
}
複製代碼
ECStack = [
globalContext
];
複製代碼
在這個過程當中,第 5 步,outter 已經執行結束,執行上下文按理來講已經被銷燬,內部函數 inner 怎麼還能訪問 outter 做用域的變量呢。
正是因爲閉包,inner 引用了它所在詞法做用域的自由變量 a,inner 的做用域鏈中仍然是完整的, 儘管 inner 在其餘地方執行,仍是返回了正確結果。
閉包中,一個很重要的特色就是,內部函數做爲一個數據被返回。這是因爲 JS 是函數式語言,函數能夠做爲參數傳遞進函數,也能夠做爲一個數據返回。函數的嵌套構成了做用域的嵌套,也就有了做用域鏈。
因爲函數具備做用域,且變量的尋找具備 「遮蔽效應」(從內到外,找到第一個就中止),使得局部做用域的變量對於外部做用域是不可見的,因而函數就有了封閉性,因此咱們拿函數來包裹封裝私有變量,同時也有了閉包。
自由變量是指在函數中使用的,但既不是函數參數也不是函數的局部變量的變量。
function outter() {
var a = 123;
function inner() {
console.log(a);
}
return inner;
}
var foo = outter();
foo(); // 123
複製代碼
對於 inner 函數而言,變量 a, 不是它的函數參數,也不是它的局部變量,a 就是自由變量。
從閉包的特色能夠看出,自由變量保存在了內存中,並能間接訪問。
那麼閉包的做用就是:
隱藏私有變量,解決變量命名空間污染的問題。
缺點
若是閉包過多,變量常駐內存,確定會佔用大量內存空間。
因爲 JS 是函數式語言,當函數記住並訪問所在詞法做用域的自由變量時,就產生了閉包,即便函數是在當前詞法做用域外執行。
閉包在 JS 代碼中很是常見,沒必要把它想得太玄乎。
歡迎關注個人我的公衆號「謝南波」,專一分享原創文章。