深入理解JS閉包
關於JS中閉包的理解,相信很多人都和筆者一樣剛開始很是困惑。筆者也是在看了很多前輩的文章後,總結出一點自己的理解。記錄與此,囿於筆者水平有限 ,若有錯誤之處,懇請不嗇賜教。
你可以在一個函式裡面巢狀另外一個函式。巢狀(內部)函式對其容器(外部)函式是私有的。它自身也形成了一個閉包。一個閉包是一個可以自己擁有獨立的環境與變數的的表示式(通常是函式,因為ES6有了塊級作用域的概念)。
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Functions(上面這句話摘自這個網址)
第一部分:初遇閉包
http://www.runoob.com/js/js-function-closures.html
什麼是閉包?閉包有什麼作用?這是我遇到閉包時的第一反應。
閉包在JavaScript高階程式設計(第3版)中是這樣描述:閉包是指有權訪問另一個函式作用域中的變數的函式。
那麼閉包的作用也就很明顯了。
1. 可以在函式的外部訪問到函式內部的區域性變數。
2. 讓這些變數始終儲存在記憶體中,不會隨著函式的結束而自動銷燬。
在上面的程式碼中,閉包指的就是function () {return counter += 1;}這個函式。首先解釋一下這段程式碼,在變數add被賦值之前,第一個function執行了一次(執行且僅會執行一次),因為這是一個函式表示式宣告方式並且宣告後加上了(),所以會自動執行一次。執行後add被賦值(匿名函式)了,add= function () {return counter += 1;} 。然後每次呼叫add()函式時,返回的都是這個函式,因為這個函式在第一個函式的內部,所以即使第一個函式執行完了,第二個函式依然能訪問counter(JS設計的作用域鏈,當前作用域能訪問上級的作用域)。
閉包是可以在另一個函式的外部訪問到其作用域中的變數的函式。而被訪問的變數可以和函式一同存在。即使另一個函式已經執行結束,導致建立變數的環境銷燬,也依然會存在,直到訪問變數的那個函式被銷燬。當然,如果僅僅是做一個簡單的計數器,大可不用這樣麻煩。下面這簡短的程式碼就能輕鬆實現。
var a = 0;
function myFunction(){
a++;
document.getElementById("demo").innerHTML = a;
}
推薦一篇部落格:https://blog.csdn.net/qq_36276528/article/details/70049825(寫得很有深度)
第二部分:牛客翻船
https://www.nowcoder.com/questionTerminal/da4115e308c948169a9a73e50d09a3e7
下面是這個題目的解答:
每個li標籤的onclick事件執行時,本身onclick繫結的function的作用域中沒有變數i,i為undefined,則解析引擎會尋找父級作用域,發現父級作用域中有i,且for迴圈繫結事件結束後,i已經賦值為4,所以每個li標籤的onclick事件執行時,alert的都是父作用域中的i,也就是4。這是作用域的問題。
閉包只能取得包含函式中任何變數的最後一個值。因為別忘了閉包所儲存的是整個變數物件,而不是某個特殊的變數。
這是在迴圈體中建立閉包的常見錯誤。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures(一定要看這篇)
這裡面給onclick賦值的是閉包。很多人會問為什麼是閉包?之前閉包不是函式A裡的函式B嗎?函式B用來訪問函式A的變數,稱函式B是閉包,題目中只有一個函式為什麼也是閉包。其實,用兩個函式形成閉包只是一般形式。閉包真正的含義是,如果一個函式訪問了此函式的父級及父級以上的作用域變數,就可以稱這個函式是一個閉包。
<script>
var a = 1;
(function test (){
alert(a);
})()
</script>
所以上面的function都可以稱之為閉包(匿名閉包函式)。
這裡還是作用域的問題,那麼我們把每次的i都儲存到一個變數中,匿名閉包就可以實現想要的效果。
var elements=document.getElementsByTagName('li');
var length=elements.length;
for(var i=0;i<length;i++){
elements[i].onclick=function(num){
return function() {
alert(num);
};
}(i);
}
這樣就使用了閉包,這裡面的閉包指的是function() {alert(num);};第二個function裡面彈出的num是第一個function的引數,通過(i)執行了這裡面的第一個函式,同時i的值被儲存到num中。每個點選事件中都有一個區域性變數num,num儲存的是相應的i值。
第三部分:let的橫空出世
上面的牛客題目只需要將for(var i=0;i<length;i++)中的var改成let就能實現想要的效果,這讓在迴圈體內建立閉包具有更好的可讀性。let的簡單介紹:https://mp.csdn.net/postedit/81065540
let的到來,讓令人詬病的JS獲得了一絲生機,也補上了JS沒有塊級作用域的短板。ECMAScript6還有很多新特性,筆者也在不斷學習中。
第四部分:閉包的應用
函式工廠和閉包模擬私有方法
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures(這篇講得很好,希望讀者們讀透)
相關文章
- 深入理解閉包
- 深入理解javascript原型和閉包(15)——閉包JavaScript原型
- js閉包的理解JS
- 深入理解swift的閉包Swift
- 對JS閉包的理解JS
- [譯]理解JS中的閉包JS
- JS-閉包(closure)的理解JS
- 深入理解javascript系列(七):閉包(1)JavaScript
- 深入理解javascript系列(八):閉包(2)JavaScript
- 深入理解javascript原型和閉包系列JavaScript原型
- 深入理解javascript原型和閉包(10)——thisJavaScript原型
- 深入學習js之——閉包#8JS
- 徹底理解js中的閉包JS
- 深入理解javascript系列(九):應用閉包JavaScript
- 深入理解javascript原型和閉包(17)——補thisJavaScript原型
- 深入理解javascript原型和閉包(完結)JavaScript原型
- 理解“閉包”
- 理解閉包
- 深入理解javascript系列(十):模組化與閉包JavaScript
- 深入理解閉包系列第三篇——IIFE
- 深入理解javascript原型和閉包(16)——完結JavaScript原型
- 深入理解javascript原型和閉包(6)——繼承JavaScript原型繼承
- 深入理解javascript原型和閉包(5)——instanceofJavaScript原型
- 深入理解javascript原型和閉包(3)——prototype原型JavaScript原型
- 理解JavaScript 閉包JavaScript
- Groovy閉包理解
- 理解 JavaScript 閉包JavaScript
- 談談我對js中閉包的理解JS
- 深入理解javascript原型和閉包(4)——隱式原型JavaScript原型
- JavaScript深入之閉包JavaScript
- JavaScript 深入之閉包JavaScript
- 深入理解javascript原型和閉包(12)——簡介【作用域】JavaScript原型
- 深入理解javascript原型和閉包(7)——原型的靈活性JavaScript原型
- java程式設計師理解js中的閉包Java程式設計師JS
- PHP 閉包的理解PHP
- 理解Javascript的閉包JavaScript
- 深入淺出Javascript閉包JavaScript
- JS閉包ClosureJS