一.什么是閉包
高級程序設計三中:閉包是指有權訪問另外一個函數作用域中的變量的函數.可以理解為(能夠讀取其他函數內部變量的函數)
閉包的作用: 正常函數執行完畢后,里面聲明的變量被垃圾回收處理掉,但是閉包可以讓作用域里的 變量,在函數執行完之后依舊保持沒有被垃圾回收處理掉
二. 閉包的實例
// 創建閉包最常見的方式函數作為返回值 function foo() { var name = "kebi"; return function() { console.log(name); }; } var bar = foo(); bar(); //打印kebi --外部函數訪問內部變量
接下來通過一個實例來感受一下閉包的作用:
接下來實現一個計數器大家肯定會覺得這不是很簡單嗎
var count = 0; function add() { count = count + 1; console.log(count); } add(); //確實實現了需求 //但是如果需要第二個計數器呢? //難道要如下這樣寫嗎? var count1 = 0; function add1() { count1 = count1 + 1; console.log(count1); } add1(); //確實實現了需求
當我們需要更多地時候,這樣明顯是不現實的,這里我們就需要用到閉包.
function addCount() { var conut = 0; return function() { count = count + 1; console.log(count); }; }
這里解釋一下上邊的過程: addCount() 執行的時候, 返回一個函數, 函數是可以創建自己的作用域的, 但是此時返回的這個函數內部需要引用 addCount() 作用域下的變量 count, 因此這個 count 是不能被銷毀的.接下來需要幾個計數器我們就定義幾個變量就可以,並且他們都不會互相影響,每個函數作用域中還會保存 count 變量不被銷毀,進行不斷的累加
var fun1 = addCount(); fun1(); //1 fun1(); //2 var fun2 = addCount(); fun2(); //1 fun2(); //2
三.常見面試題
1. for 循環中打印
for (var i = 0; i < 4; i++) { setTimeout(function() { console.log(i); }, 300); }
上邊打印出來的都是 4, 可能部分人會認為打印的是 0,1,2,3
原因:js 執行的時候首先會先執行主線程,異步相關的會存到異步隊列里,當主線程執行完畢開始執行異步隊列, 主線程執行完畢后,此時 i 的值為 4,說以在執行異步隊列的時候,打印出來的都是 4(這里需要大家對 event loop 有所了解(js 的事件循環機制))
如何修改使其正常打印:(使用閉包使其正常打印)
//方法一: for (var i = 0; i < 4; i++) { setTimeout( (function(i) { return function() { console.log(i); }; })(i), 300 ); } // 或者 for (var i = 0; i < 4; i++) { setTimeout( (function() { var temp = i; return function() { console.log(temp); }; })(), 300 ); }
這個是通過自執行函數返回一個函數,然后在調用返回的函數去獲取自執行函數內部的變量,此為閉包
//方法發二: for (var i = 0; i < 4; i++) { (function(i) { setTimeout(function() { console.log(i); }, 300); })(i); }
大部分都認為方法一和方法二都是閉包,我認為方法一是閉包,而方法二是通過創建一個自執行函數,使變量存在這個自執行函數的作用域里
2.真實的獲取多個元素並添加點擊事件
var op = document.querySelectorAll("p"); for (var j = 0; j < op.length; j++) { op[j].onclick = function() { alert(j); }; } //alert出來的值是一樣的 // 解決辦法一: for (var j = 0; j < op.length; j++) { (function(j) { op[j].onclick = function() { alert(j); }; })(j); } // 解決辦法二: for (var j = 0; j < op.length; j++) { op[j].onclick = (function(j) { return function() { alert(j); }; })(j); } //解決方法三其實和二類似 for (var j = 0; j < op.length; j++) { op[j].onclick = (function() { var temp = j; return function() { alert(j); }; })(); }
這個例子和例子一幾乎是一樣的大家可以參考例子一
3.閉包的缺陷:
通過上邊的例子也發現, 閉包會導致內存占用過高,因為變量都沒有釋放內存