1、閉包就是指有權訪問另一個函數作用域中的變量的函數,這句話有兩個點,1閉包是函數,2作用域。
有了這兩個條件我們能聯想到的就是js的執行環境。函數的執行環境依賴於變量作用域,在js中這個作用域是函數定義時決定的,而不取決於調用時。通過如下案例來解釋:
var a = 'hello' //全局變量
function fun() {
var a = 'world' //局部變量
function fn() {
return a
}
return fn()
}
console.log(fun()) //world
var a = 'hello'
function fun() {
var a = 'world'
function fn() {
return a
}
return fn
}
console.log(fun()()) //world
//函數fun返回值fn,fn執行過程中取得a的值為fn定義時的值,而並非取得全局變量
如果按照正常的說法,函數在執行完畢后,其內部相關的作用域鏈會被回收,而閉包恰巧就是鑽了空子,使得函數中的作用域鏈再次其內部的變量不被銷毀,所以在有些場合,尤其是循環操作DOM的過程中,過度的使用閉包,可能會造成內存泄漏。
2、通過上面的案例,我們認識到,閉包就相當於一個塊,這個塊里包含所定義時的作用域。現在嘗試用閉包寫一個計數器
function sum() {
var a = 3 //私有變量
return {
add: function(k) {
return a + k
},
del: function(k) {
return k - a
}
}
}
var i = sum()
var j = sum()
console.log(i.add(1)) //4
console.log(j.add(3)) //6
console.log(i.del(9)) //6
console.log(j.del(6)) //3
我們開始定義了個sum函數,返回兩個add和del函數形成閉包,在對其兩個函數進行分別操作時,發現i,j變量分別控制的是不同的兩個結果,這就表明,兩個方法 都可以訪問私有變量 a 而每次調用add都回創建一個新的作用域鏈和一個新的私有變量,可以多次重復使用,不會影響到彼此。但是兩者都僅僅局限sum函數創建下,現在對其進行改造寫法:如下:
(function(w) {
var a = 3
this.add = function(k) {
return a + k
}
this.del = function(k) {
return k - a
}
}(window))
console.log(add(1)) //4
console.log(add(3)) //6
console.log(del(9)) //6
console.log(del(6)) //3
結果和上述一致,區別在於被調用的對象有不同,我們通過一個匿名函數來把我們要執行的函數添加到window對象上,來達到全局變量的目的, 我們可以這樣理解,一個匿名函數就相當於一個大的作用域,而內部的變量每次都只能在內部調用,其他外界函數是訪問不到的,這樣就保證了一個獨立的作用域鏈.
3、總結閉包的優缺點
* 優點 1、使用閉包可以通過外部函數訪問內部的函數值,提升作用域,便於鏈式調用
* 優點 2、內部的變量可以多次重復使用,不會造成全局的變量污染。
* 優點 3、全局變量可能會造成命名沖突,使用閉包不用擔心這個問題,因為它是私有化,加強了封裝性,有效防止了
* 缺點 1、閉包創建的變量都被保存在內存中,內存消耗很大,處理不當,IE中容易造成內存泄漏。