閉包是javascript語言的一個難點,也是它的特點,不少高級應用都要依靠閉包來實現。我的的理解是:函數中嵌套函數。javascript
閉包是指有權訪問另外一個函數做用域中的變量的函數。建立閉包的常見方式,就是在一個函數內部建立另外一個函數。html
閉包的缺點是常駐內存,會增大內存的使用量,使用不當會形成內存泄漏。java
應用閉包主要是爲了:設計私有變量和方法。node
通常來說,函數執行完畢後,局部活動對象就會被銷燬,內存中僅保存全局做用域,可是閉包的狀況有所不一樣!瀏覽器
做用域鏈:當代碼在執行過程當中,會建立變量對象的一個做用域鏈。做用域鏈的用途,是保證對執行環境有權訪問的全部變量和函數的有序訪問。
請看下面的一段代碼:閉包
//全局環境中有一個變量color和一個函數changeColor() var color = "blue"; //changeColor()的局部環境中有一個anotherColor變量和swapColors()函數 function changeColor() { var anotherColor = "red"; //swapColors()環境中只有一個tempColor function swapColors() { var tempColor = anotherColor; anotherColor = color; color = tempColor; } swapColors(); } changeColor();
全局環境只能訪問到變量color
changeColor()局部環境也能夠訪問color
swapColors()能夠訪問其餘兩個環境的全部變量,可是那兩個變量都無權訪問tempColor模塊化
總結:內部環境能夠經過做用域鏈訪問全部的外部環境,但外部環境不能訪問內部環境中的任何變量和函數。每一個環境均可以向上搜索做用域鏈,但任何環境都不能向下搜索做用域鏈而進入另外一個執行環境。函數
垃圾回收原理學習
(1)javascript中若是一個對象再也不被引用,那麼這個對象就會被回收。
(2)若是兩個對象互相引用,而再也不被第3者引用,那麼這兩個互相引用的對象也會被回收。this
嵌套函數的閉包
var f = function () { var a = 9999; function f1() { alert(a); } f1(); }; f();
函數嵌套時候,在f執行完成以後,變量a還要被f1這個內部嵌套的函數繼續使用,所以a不會被釋放。js解析器發現函數中嵌套了函數時,就會把函數中的變量和子函數的變量一塊兒保存,構成了一個「閉包」。這些變量不會被內存回收器回收,只有當內部嵌套的函數不在執行後,纔會被回收。
閉包有三個特性:
1.函數嵌套函數
2.函數內部能夠引用外部的參數和變量
3.參數和變量不會被垃圾回收機制回收
使用閉包的好處:
1.但願一個變量長期駐紮內存
2.避免全局變量污染
3.私有成員變量的存在
屬性
var person = function () { var name = "kimi"; this.getName = function () { return name; }; }; var p = new person(); alert(p.getName());
name屬性經過getName方法獲取到。
在循環中直接找到對應元素的索引
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script type="text/javascript" charset="UTF-8"> window.onload = function () { var aLi = document.getElementsByTagName('li'); for (var i = 0; i < aLi.length; i++) { aLi[i].onclick = function () { //當點擊時for循環已經結束 alert(i); }; } } </script> </head> <body> <ul> <li>a</li> <li>b</li> <li>c</li> <li>d</li> </ul> </body> </html>
執行以上代碼發現點擊任何一個返回的都是4,這是由於賦值的時候,傳的i是對內存地址的引用,循環結束,i指向的就是4.
使用閉包改寫上面的代碼
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>閉包</title> <script type="text/javascript" charset="UTF-8"> window.onload = function () { var aLi = document.getElementsByTagName('li'); for (var i = 0; i < aLi.length; i++) { (function (i) { aLi[i].onclick = function () { alert(i); }; })(i); } }; </script> </head> <body> <ul> <li>a</li> <li>b</li> <li>c</li> <li>d</li> </ul> </body> </html>
每一次循環的時候,都把當前的i經過當即執行函數賦值。
全局變量的累加
<script type="text/javascript" charset="UTF-8"> var i = 1; function text() { i++; alert(i); } text();//2 text();//3 </script>
局部變量的累加
<script type="text/javascript" charset="UTF-8"> function text() { var i = 1; i++; alert(i); } text();//2 text();//2 </script>
上述代碼沒有實現累加,改寫代碼以下:
<script type="text/javascript" charset="UTF-8"> function text() { var i = 1; return function () {//函數嵌套 i++; alert(i); } } var a = text();//外部函數賦給a a();//2 a();//3 </script>
<script type="text/javascript" charset="UTF-8"> var g = (function () { var i = 1; return function () { i++; alert(i); } })(); g();//2 調用一次g函數,其實調用的是裏面內部函數的返回值 g();//3 </script>
在閉包中使用this對象可能致使一些問題
<script type="text/javascript" charset="UTF-8"> var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { return function () { return this.name; }; } }; alert(object.getNameFunc()());//The Window </script>
代碼先建立了一個全局變量name,又建立了一個包含name屬性的對象。這個對象還包含一個getNameFunc()方法,返回一個匿名函數,匿名函數又返回一個this.name。調用object.getNameFunc()()返回一個字符串。內部函數搜索的時候只搜索到活動對象。
<script type="text/javascript" charset="UTF-8"> var name = "The Window"; var object = { name: "My Object", getNameFunc: function () { var that = this; return function () { return that.name; }; } }; alert(object.getNameFunc()());//My Object </script>
在定義匿名函數前,把this對象賦值給that變量,閉包也能夠訪問這個變量。即便函數返回,仍然引用着object
學習了閉包也不知道到底哪裏用到,到底有什麼用。回答:(其實你寫的每個js函數都是閉包,一個js函數的頂層做用域就是window對象,js的執行環境自己就是一個scope(瀏覽器的window/node的global),咱們一般稱之爲全局做用域。每一個函數,不論多深,均可以認爲是全局scope的子做用域,能夠理解爲閉包。)
本篇文章是本身學習過程當中的總結,若有錯誤歡迎指正。