JS前端閉包是什麼?私有變數可以用到閉包。
什麼是閉包?
百度百科定義:閉包就是能夠讀取其他函式內部變數的函式。建立閉包的通常方式,是在一個函式內部建立另一個函式
最常見的閉包結構如下
function
aaa
(){
var
name
=
“xxx”
return
function
bbb
(){
alert
(
name
);
}
}
如上程式碼,bbb函式內可以訪問aaa函式作用域內的變數
閉包解決了什麼問題?
1。 可以讀取函式內部的變數;
2。 讓這些變數的值始終保持在記憶體中。不會在函式呼叫後被清除;
閉包的定義及其優缺點
閉包
是指有權訪問另一個函式作用域中的變數的函式,建立閉包的最常見的方式就是在一個函式內建立另一個函式,透過另一個函式訪問這個函式的區域性變數
閉包的缺點就是常駐記憶體,會增大記憶體使用量,使用不當很容易造成記憶體洩露。
閉包是
javascript
語言的一大特點,主要應用閉包場合主要是為了:設計私有的方法和變數。
一般函式執行完畢後,區域性活動物件就被銷燬,記憶體中僅僅儲存全域性作用域。但閉包的情況不同!
avascript
的垃圾回收原理
(1)、在
javascript
中,如果一個物件不再被引用,那麼這個物件就會被
GC
回收;
(2)、如果兩個物件互相引用,而不再被第
3
者所引用,那麼這兩個互相引用的物件也會被回收。
使用閉包的好處
那麼使用閉包有什麼好處呢?使用閉包的好處是:
1。希望一個變數長期駐紮在記憶體中
2。避免全域性變數的汙染
3。私有成員的存在
全域性變數
函式可以訪問由函式內部定義的變數,如:
function
myFunction
()
{
var
a
=
4
;
return
a
*
a
;
}
函式也可以訪問函式外部定義的變數,如:
var
a
=
4
;
function
myFunction
()
{
return
a
*
a
;
}
計數器困境
設想下如果你想統計一些數值,且該計數器在所有函式中都是可用的。
你可以使用全域性變數,函式設定計數器遞增:
var
counter
=
0
;
function
add
()
{
return
counter
+=
1
;
}
add
();
add
();
add
();
// 計數器現在為 3
但問題來了,頁面上的任何指令碼都能改變計數器,即便沒有呼叫 add() 函式。
如果我在函式內宣告計數器,如果沒有呼叫函式將無法修改計數器的值:
function
add
()
{
var
counter
=
0
;
return
counter
+=
1
;
}
add
();
add
();
add
();
// 本意是想輸出 3, 但事與願違,輸出的都是 1 !
JavaScript 巢狀函式
所有函式都有權訪問全域性作用域。
事實上,在 JavaScript 中,所有函式都有權訪問它們“上面”的作用域。
JavaScript 支援巢狀函式。巢狀函式可以訪問其上的作用域。
在本例中,內部函式 plus() 可以訪問父函式中的 counter 計數器變數:
function
add
()
{
var
counter
=
0
;
function
plus
()
{
counter
+=
1
;}
plus
();
return
counter
;
}
這樣即可解決計數器困境,如果我們能夠從外面訪問 plus() 函式。
我們還需要找到只執行一次 counter = 0 的方法。
我們需要閉包(closure)。
JavaScript 閉包
還記得函式自我呼叫嗎?該函式會做什麼?
var
add
=
(
function
()
{
var
counter
=
0
;
return
function
()
{
return
counter
+=
1
;}
})();
add
();
add
();
add
();
// 計數器為 3
例子解釋
變數 add 的賦值是自呼叫函式的返回值。
這個自呼叫函式只執行一次。它設定計數器為零(0),並返回函式表示式。
這樣 add 成為了函式。最“精彩的”部分是它能夠訪問父作用域中的計數器。
這被稱為 JavaScript
閉包
。它使函式擁有“
私有
”變數成為可能。
計數器被這個匿名函式的作用域保護,並且只能使用 add 函式來修改。
閉包指的是有權訪問父作用域的函式,即使在父函式關閉之後。
技巧1: 用閉包解決遞迴呼叫問題
function
factorial
(
num
)
{
if
(
num
<=
1
)
{
return
1
}
else
{
return
num
*
factorial
(
num
-
1
)
}
}
var
anotherFactorial
=
factorial
factorial
=
null
anotherFactorial
(
4
)
// 報錯 ,因為最好是return num* arguments。callee(num-1),arguments。callee指向當前執行函式,但是在嚴格模式下不能使用該屬性也會報錯,所以藉助閉包來實現
// 使用閉包實現遞迴
function
newFactorial
=
(
function
f
(
num
){
if
(
num
<
1
)
{
return
1
}
else
{
return
num
*
f
(
num
-
1
)
}
})
//這樣就沒有問題了,實際上起作用的是閉包函式f,而不是外面的函式newFactorial
技巧2:用閉包模仿塊級作用域
es6沒出來之前,用var定義變數存在變數提升問題:
for
(
var
i
=
0
;
i
<
10
;
i
++
){
console
。
info
(
i
)
}
alert
(
i
)
// 變數提升,彈出10
//為了避免i的提升可以這樣做
(
function
()
{
for
(
var
i
=
0
;
i
<
10
;
i
++
){
console
。
info
(
i
)
}
})()
alert
(
i
)
// underfined 因為i隨著函式的退出,執行環境銷燬,變量回收