概念:當一個內部函數被調用,就會形成閉包,閉包就是能夠讀取其他函數內部變量的函數
就是一個函數去訪問了另外一個函數的中的變量的函數
例子:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>閉包</title> </head> <body> <script type="text/javascript"> //允許函數中嵌套函數 //內部函數允許調用外部函數的變量 //閉包就是能夠讀取其他函數內部變量的函數,內部函數和執行的上下文 var foo=function(){ var n=1; return function(){ n=n+1; console.log(n); } } var bar=foo(); bar(); //2 bar(); //3 var foobar=foo(); foobar(); //2 foobar(); //3 </script> </body> </html>
運行結果:
閉包作用:局部變量無法共享和長久的保存,而全局變量可能造成變量污染,所以我們希望有一種機制既可以長久的保存變量又不會造成全局污染。延伸變量的作用范圍。
閉包特點:占用更多內存;不容易被釋放
閉包用法:變量既想反復使用,又想避免全局污染如何使用?
1.定義外層函數,封裝被保護的局部變量。
2.定義內層函數,執行對外部函數變量的操作。
3.外層函數返回內層函數的對象,並且外層函數被調用,結果保存在一個全局的變量中。
例子:
function getCounter() { var n = 1; var inner = function () { return n++; } return inner; } var getNum = getCounter(); getNum(); // n = 2; /*// 類似於 var n = 1; var getNum = function () { return n++; } getNum(); // n = 2; // 當調用getNum 就獲取到了n的值*/
實用例子:看下面代碼
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>閉包</title> </head> <button>按鈕1</button> <button>按鈕2</button> <button>按鈕3</button> <button>按鈕4</button> <button>按鈕5</button> <button>按鈕6</button> <button>按鈕7</button> <button>按鈕8</button> <button>按鈕9</button> <body> <script type="text/javascript"> // 閉包的作用 var btn = document.querySelectorAll('button'); for (var i = 0; i < btn.length; i++) { btn[i].onclick = function() { alert(i); } } </script> </body> </html>
結果是,點擊任何按鈕都是彈出9,和我們預想的結果點擊按鈕1輸出0,按鈕3輸出2,的不一樣。
因為我們的function是一個一部任務,只有點擊了才會執行,當循環結束了,i就等於4了,所以點擊按鈕,執行function但是已經等於4了。
解決方法:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>閉包</title> </head> <button>按鈕1</button> <button>按鈕2</button> <button>按鈕3</button> <button>按鈕4</button> <button>按鈕5</button> <button>按鈕6</button> <button>按鈕7</button> <button>按鈕8</button> <button>按鈕9</button> <body> <script type="text/javascript"> // 閉包的作用 var btn = document.querySelectorAll('button'); for (var i = 0; i < btn.length; i++) { btn[i].index = i; btn[i].onclick = function() { alert(this.index); } } </script> </body> </html>
給每個btn都綁定一個index屬性,並把i賦值給index,當點擊了再獲取。
當然也可以使用閉包來解決這個問題
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>閉包</title> </head> <button>按鈕1</button> <button>按鈕2</button> <button>按鈕3</button> <button>按鈕4</button> <button>按鈕5</button> <button>按鈕6</button> <button>按鈕7</button> <button>按鈕8</button> <button>按鈕9</button> <body> <script type="text/javascript"> // 閉包的作用 var btn = document.querySelectorAll('button'); for (var i = 0; i < btn.length; i++) { (function (a) { btn[i].onclick = function() { alert(a); } })(i); } </script> </body> </html>
這里面寫了一個立即執行函數,把i的值傳入到立即執行函數里面,那么立即執行表達函數就有了一個變量
// 閉包的作用 var btn = document.querySelectorAll('button'); for (var i = 0; i < btn.length; i++) { // 立即執行表達函數 (function (a) { // 這里是一個function,其中有個變量a btn[i].onclick = function() { // 這里也是一個function alert(a); } })(i); // 把i傳入立即執行函數 }
實用案例2:
var btn = document.querySelectorAll('button'); for (var i = 0; i < btn.length; i++) { setTimeout(function() { console.log(btn[i].innerHTML); }, 3000) }
for循環是一個同步任務,會立馬執行,但是setTimeout是一個異步的,里面的function是一個回調函數,只有執行了setTimeout才會執行這個function,所以我執行這個setTimeout的時候 i 變成了9,我只有8個元素,所以出錯了。
使用閉包:
var btn = document.querySelectorAll('button'); for (var i = 0; i < btn.length; i++) { (function(a) { setTimeout(function() { console.log(btn[a].innerHTML); }, 3000); })(i); }