您好,登錄后才能下訂單哦!
怎樣理解JavaScript 閉包,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
深入剖析 JavaScript 閉包
?一個函數和對其周圍狀態的引用捆綁在一起,這樣的組合就是「閉包」.
通俗的說:一個內層函數可以訪問外層函數的作用域 就叫 「閉包」。
在 JavaScript 中,每當創建一個函數,閉包就會在函數創建的同時被創建出來。
閉包的形成與變量的作用域以及變量的生命周期密切相關。
?
??
函數嵌套函數
函數內部可以引用外部的參數和變量
參數和變量不會被垃圾回收機制回收
?優點:
?可以設計私有的方法和變量
?「缺點」
?常駐內存,會增大內存使用量,使用不當很容易造成內存泄露。
?「一般函數執行完畢后,局部活動對象就被銷毀,內存中僅僅保存全局作用域。」
?
?變量的作用域:變量的有效范圍。
在實際開發中,我們經常遇到的是 「函數中聲明的變量作用域。」
?
var a = '閉包';
function getValue(){
var a = '函數局部作用域'
console.log(a)
}
getValue() //函數局部作用域
?當在全局聲明了一個同名變量,在函數內部也聲明了一個同名變量,函數優先訪問函數作用域中的變量。
?
?函數作用域:在函數內部可以訪問到函數外部變量,而在函數外部的變量不可以訪問函數內部的變量。
為什么呢?
「因為當在函數中搜索一個變量的時候,如果函數內部沒有這個變量的聲明,那么它會隨著代碼的執行環境創建的作用域往外層逐層搜索,直到搜索到全局變量為止。」
變量的搜索是從內到外搜索的。
?
function getData() {
var str = "閉包練習";
var fun = function(){
var innerStr = '內部變量'
}
console.log(innerStr)
//innerStr is not defined 函數外層是訪問不到 函數內層變量的
}
getData()
?對于 「全局變量」,它的生存周期是永久的的,除非主動銷毀變量。
而對于 「函數局部變量」 ,當函數執行完畢,局部變量也就銷毀了。
?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<script>
var nodes = document.getElementsByTagName('div')
for (var i = 0; i < nodes.length; i++) {
nodes[i].onclick = function () {
alert(i)
}
}
</script>
</body>
</html>
?給每個
div
增加點擊事件,當點擊 div 時,彈出它對應的索引值。現在無論點擊哪個
div
,它 彈出的 都是 4 。為什么呢?
「因為 div 點擊事件 是被 異步觸發的,當事件被觸發的時候,循環已經執行完,此時的 i 的 變量值 為 4。」
如何解決 點擊每個div 彈出對應的i 值呢 ?
**可以借用 閉包, 把每次循環的 i 保存起來,當執行點擊事件時,它會從內到外 搜索變量的作用域,它會優先搜索到 閉包環境環境的 i **
?
# 閉包解決辦法
<script>
var nodes = document.getElementsByTagName('div')
for (var i = 0; i < nodes.length; i++) {
(function(i) {
nodes[i].onclick = function () {
alert(i)
}
})(i)
}
</script>
var num = 1;
function getValue(){
var num = 0;
return function(){
num++
console.log(num)
}
}
var s = getValue()
s()
s()
// 1 2
?按常理思路來:函數執行完畢,num = 1 銷毀,變為初始值 num = 0 ,變量在函數中作用域從內到外逐層搜索。
前面也說到了,當函數執行完,局部變量也跟著銷毀了,那為什么會 輸出 2 呢 ?
?這里 涉及到 垃圾回收機制引用計數問題
[關于垃圾回收] https://blog.csdn.net/zhouziyu2011/article/details/61201613
簡述:
「當聲明了一個變量并將一個引用類型值賦給該變量時,則該值的引用次數就是1;如果同一個值又被賦給另一個變量,則該值的引用次數加1;如果包含對該值引用的變量又取得了另外一個值,則該值的引用次數減1。當該值的引用次數變為0時,則可以回收其占用的內存空間。當垃圾回收器下一次運行時,就會釋放那些引用次數為0的值所占用的內存。」
?「解答」
?第一次執行
s()
時,num = 1第二次 執行
?s()
時, 由于 引用的時第一次s ()
的變量num=1,num 沒有被銷毀,固然在 num = 1 的基礎上 再 加 1 。「注意」
如果沒有使用同樣引用的話,那么多次調用,都是同樣的值,因為沒有記錄引用值。
函數在執行完畢,num = 1 被銷毀掉了,初始為 0
?
var num = 1;
function getValue(){
var num = 0;
return function(){
num++
console.log(num)
}
}
getValue()()
getValue()()
// 0 0
?閉包的注意作用為這兩項:
?
「可以讀取函數內部的變量」 「可以變量的值始終保持在內存中」
function f2(){
let num = 0;
addNum = function(){
num++
}
function f3(){
console.log(num)
}
return f3
}
var a = f2()
a()
addNum()
a()
// 0 1
?
結果為 0 1
函數在執行完畢,局部變量也跟著銷毀, 結果 不應該是 0 0 嗎 ?
其實a() 相當于 是 f3() 的閉包函數,它被執行了兩次。
第一次 執行 a() 時, 結果為 0 , 很好理解。 第二次 執行的 f2()
函數內部的addNum
函數,發現沒這個匿名函數賦值給一個變量,而且這個變量沒加var / let
, 那么它此時的作用域為全局
,保存在內存當中。執行addNum
時它訪問的f2()
函數內部的局部變量num
, 此時,addNum
的存在依賴于f2
,因此f2
也在內存中,不會在調用結束后,被垃圾回收機制(garbage collection)回收。第三次 執行 a()
時, 因為num
已存在內存中,而 值為1最終輸出結果:0 , 1
?
?
由于閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。解決方法是,在退出函數之前,將不使用的局部變量全部刪除。 閉包會在父函數外部,改變父函數內部變量的值。所以,如果你把父函數當作對象(object)使用,把閉包當作它的公用方法(Public Method),把內部變量當作它的私有屬性(private value),這時一定要小心,不要隨便改變父函數內部變量的值。
看完上述內容,你們掌握怎樣理解JavaScript 閉包的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。