當javaScript從入門到提高前需要註意的細節:閉包部分 – Javascript教程_JS教程_技術文章 – 程式設計聯盟

對於希望在javascript技術中提高的人群來說,閉包肯定時常是一個令人感覺神秘的技術。早先有人說javaScript中的閉包可能會引發javaScript內存管理的復雜度,也許會出現內存泄露,所以不建議用閉包。不過jQuery很好的證明瞭閉包非常好用,C#的Linq也證明的閉包技術的重要性,所以花一點點時間來理解下閉包還是很值得的,再說瞭,以下的內容不過就是一杯茶的時間而已。

先給出一個閉包的定義:在計算機科學中,閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用瞭自由變量的函數。這個被引用的自由變量將和這個函數一同存在,即使已經離開瞭創造它的環境也不例外。所以,有另一種說法認為閉包是由函數和與其相關的引用環境組合而成的實體。

以上定義中非常重要的是

閉包關聯到:函數和變量

閉包鎖定瞭:函數和變量的環境關系

 

常有人在說閉包前會給出類似以下案例,說是javaScript的特殊性,函數中訪問全局變量,這個我一直不太明白,一個函數訪問它所在環境中的全局變量很特殊很奇怪嗎?

[javascript]
var a = 10; 
function fun1() { 
    alert(a); 

 
 
fun1(); 

而且就算有以下代碼我也絲毫不感覺有任何問題
[javascript]
var a = 10; 
 
function fun2() { 
    b = 100;// 全局變量 

 
function fun1() { 
    alert(a); 
    alert(b); 

 
fun2(); 
fun1(); 

真正值得你考量的是以下代碼
[javascript]
function fun1(x) { 
    var a = x; 
    function fun2() { 
        return a; 
    } 
    return fun2; 

 
 
alert(fun1(100)()); //100 
alert(fun1(9)()); //9 

如果從結果來看,你也許同樣不屑一顧,返回的值很明確的嘛。但是你仔細想想,問題就不簡單瞭
函數fun1的結果是返回瞭一個fun2的函數。從代碼來講,fun1()是調用瞭fun1的執行,並且得到瞭fun2的函數,fun1()()就是說是執行fun2()瞭!如果你還是感覺正常的話,要麼說明你已經理解閉包瞭,要麼就是你忽略瞭一個重要的事實!

調用fun1()後,fun1所占用的內存應該已經釋放瞭,fun1函數中的所有變量都將釋放!!!對不?

[javascript]
function sum(x, y) { 
    var a = x, b = y; 
    return a + b; 

 
 
alert(sum(9, 5));//14 

上面的代碼,當我完成sum(9,5)之後,sum函數肯定被釋放瞭,a和b將不存在是不?如果你還是感覺不是很明晰的話,那麼看下如下的分解動作吧。
[javascript]
function fun1(x) { 
    var a = x; 
    function fun2() { 
        return a; 
    } 
    return fun2; 

 
 
var fun3 = fun1(10); 
var fun4 = fun1(9); 
alert(fun4()); //9 
alert(fun3()); //10 

看看第一次的fun1將x的值賦值瞭10,第二次的fun1將x的值賦值為9,而且我們先執行瞭fun4,要求返還的值是9,再次執行fun3的得到的是10!!!說明什麼呢?說明當fun2被創建的時候,它將它所需要用到的變量鎖住 瞭,或者不這麼誇張的說是記憶住瞭。
閉包就是函數在創建自己的時候,將需要用的變量鎖住或說記憶住。

這裡的函數創建不是指函數聲明,而且指函數表達式被激活的時候,匿名函數表達式的激活有:call就是()調用,()分組,還有就是return的時候。

看看如下案例

[javascript]
var dofun = []; 
 
for (var i = 0; i < 10; i++) { 
    dofun[i] = function() { 
        return i; 
    } 
 

 
for (var j = 0; j < 10; j++) { 
    alert(dofun[j]()); //全部返還10 

很多人在網上用過這個案例來說明閉包的特性,我需要很嚴肅的聲明兩個問題
1 這個案例可以說明閉包

2 這個案例還有更神奇的特性說明

先說下閉包,因為當這個返回的匿名函數隻有在www.aiwalls.com

[javascript]
dofun[j]() 
的時候才被激活,這個時候i的值是10,所以這個函數被返回瞭10,要解決這個問題,可以要求函數在i是各種值的時候被激活,怎麼激活?return
[javascript]
var dofun = []; 
 
for (var i = 0; i < 10; i++) { 
    dofun[i] = (function(k) { 
        return function() { 
            return k; 
        } 
    }(i)); 
 

 
for (var j = 0; j < 10; j++) { 
    alert(dofun[j]()); //全部返還10 

這個處理中很有意思的是,我需要一個k來幫忙,為什麼呢?

那就是我剛才說的第2點,先看下如下很令人無語的代碼

[javascript]
var dofun = []; 
 
for (var i = 0; i < 10; i++) { 
    dofun[i] = function() { 
        return i; 
    } 
 

 
for (var i = 0; i < 10; i++) { 
    alert(dofun[i]()); 

咋一看,你估計會暴跳起來,這不是耍人嘛,結果我們已經知道瞭,都是10!!!!錯!!!!!!彈出的分別是0 1 2 3 4 5 6 7 8 9!!!
不相信你就測試一下看看。你仔細看看代碼不同在哪裡?下面的for用瞭變量是i,這就是不同所在,也是閉包的關鍵所在!

函數是不是值得來鎖定一個變量,是看該變量在調用這個函數的時候,是不是能在上下文作用域中找到這個變量,如果無法在調用時找到這個變量,內部函數就會鎖住它,否則就不會鎖住,至少表面上是這樣的。

現在我們再把目光移到最初的那個案例

[javascript]
function fun1(x) { 
    a = x; 
    function fun2() { 
        return a; 
    } 
    return fun2; 

 
 
var fun3 = fun1(10); 
var fun4 = fun1(9); 
alert(fun4()); //9 
a = 999; 
alert(fun3()); //999 

fun3()的結果是999的原因倒不是在乎現在a是全局變量,而是現在fun3在執行的時候能在上下文作用域中找到a瞭,全局變量不過是湊齊罷瞭。

摘自 shyleoking的專欄

發佈留言