天天看點

當javaScript從入門到提高前需要注意的細節:閉包部分

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

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

以上定義中非常重要的是

閉包關聯到:函數和變量

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

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

var a = 10;  

function fun1() {  

    alert(a);  

}  

fun1(); 

而且就算有以下代碼我也絲毫不感覺有任何問題

function fun2() {  

    b = 100;// 全局變量  

    alert(b);  

fun2();  

真正值得你考量的是以下代碼

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函數中的所有變量都将釋放!!!對不?

function sum(x, y) {  

    var a = x, b = y;  

    return a + b;  

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

上面的代碼,當我完成sum(9,5)之後,sum函數肯定被釋放了,a和b将不存在是不?如果你還是感覺不是很明晰的話,那麼看下如下的分解動作吧。

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的時候。

看看如下案例

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 這個案例還有更神奇的特性說明

先說下閉包,因為當這個傳回的匿名函數隻有在

dofun[j]() 

的時候才被激活,這個時候i的值是10,是以這個函數被傳回了10,要解決這個問題,可以要求函數在i是各種值的時候被激活,怎麼激活?return

    dofun[i] = (function(k) {  

        return function() {  

            return k;  

        }  

    }(i));  

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

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

    alert(dofun[i]());  

咋一看,你估計會暴跳起來,這不是耍人嘛,結果我們已經知道了,都是10!!!!錯!!!!!!彈出的分别是0 1 2 3 4 5 6 7 8 9!!!

不相信你就測試一下看看。你仔細看看代碼不同在哪裡?下面的for用了變量是i,這就是不同所在,也是閉包的關鍵所在!

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

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

    a = x;  

a = 999;  

alert(fun3()); //999 

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

本文轉自shyleoking 51CTO部落格,原文連結:http://blog.51cto.com/shyleoking/803076

繼續閱讀