閉包
能夠訪問另一個函數作用域的變量的函數。清晰的講:閉包就是一個函數,這個函數能夠訪問其他函數的作用域中的變量。
下面inner 就是一個閉包函數,因為他能夠訪問到outer函數的作用域
1 function outer() { 2 var a = '變量1'
3 var inner = function () { 4 console.info(a) 5 } 6 return inner; 7 }
閉包是站在作用域的角度上來定義的,因為inner訪問到outer作用域的變量,所以inner就是一個閉包函數。雖然定義很簡單,但是有很多坑點,比如this指向、變量的作用域,稍微不注意可能就造成內存泄露。
坑點一,引用的變量可能發生變化
1 function outer() { 2 var result = []; 3 for (var i = 0; i<10; i++){ 4 result.[i] = function () { 5 console.info(i) 6 } 7 } 8 return result 9 }
看樣子result每個閉包函數對打印對應數字,1,2,3,4,...,10, 實際不是,因為每個閉包函數訪問變量i是outer執行環境下的變量i,隨着循環的結束,i已經變成10了,所以執行每個閉包函數,結果打印10, 10, ..., 10。這時,可以使用閉包保存臨時數據:
1 function outer() { 2 var result = []; 3 for (var i = 0; i<10; i++){ 4 result.[i] = function (num) { 5 return function() { 6 console.info(num); 7 } 8 }(i) 9 } 10 return result 11 }
此時訪問的num,是上層函數執行環境的num,數組有10個函數對象,每個對象的執行環境下的number都不一樣。
坑點二,this指向問題
1 var object = { 2 name: ''object", 3 getName: function() { 4 return function() { 5 console.info(this.name) 6 } 7 } 8 } 9 object.getName()() // underfined
因為里面的閉包函數是在window作用域下執行的,也就是說,this指向windows
坑點三,內存泄露問題
1 function showId() { 2 var el = document.getElementById("app") 3 el.onclick = function(){ 4 aler(el.id) 5 } 6 }
這樣會導致閉包引用外層的el,當執行完showId后,el無法釋放,因此需要手動置空
1 function showId() { 2 var el = document.getElementById("app") 3 var id = el.id 4 el.onclick = function(){ 5 aler(id) 6 } 7 el = null // 主動釋放el
8 }
技巧1,用閉包解決遞歸調用問題
function factorial(num) { if(num<= 1) { return 1; } else { return num * factorial(num-1) } } var anotherFactorial = factorial factorial = null anotherFactorial(4)
報錯 ,因為最好是return num* arguments.callee(num-1),arguments.callee指向當前執行函數,但是在嚴格模式下不能使用該屬性也會報錯,所以借助閉包來實現。
1 function newFactorial = (function f(num){ 2 if(num<1) {return 1} 3 else { 4 return num* f(num-1) 5 } 6 })
這樣就沒有問題了,實際上起作用的是閉包函數f,而不是外面的函數newFactorial。
技巧2,用閉包模仿塊級作用域
es6沒出來之前,用var定義變量存在變量提升問題,當然現在大多用es6的let 和const 定義。
1 for(var i=0; i<10; i++){ 2 console.info(i) 3 } 4 alert(i) // 變量提升,彈出10
5
6 //為了避免i的提升可以這樣做
7 (function () { 8 for(var i=0; i<10; i++){ 9 console.info(i) 10 } 11 })() 12 alert(i) // underfined 因為i隨着閉包函數的退出,執行環境銷毀,變量回收