js閉包【前端開發必備】

先從閉包特點解釋,應該更好理解.閉包的兩個特點:

1、作爲一個函數變量的一個引用 - 當函數返回時,其處於激活狀態。

2、一個閉包就是當一個函數返回時,一個沒有釋放資源的棧區。

其實上面兩點可以合成一點,就是閉包函數返回時,該函數內部變量處於激活狀態,函數所在棧區依然保留.

我們所熟知的主流語言,像C,java等,在函數內部只要執行了return,函數就會返回結果,然後內存中刪除該函數所在的區域.生命週期也就停止了.一般的js函數也是這樣.

但是有閉包特性的js函數有點特殊.

就例子來說:function a(){

var i=0;

function b(){ alert(++i); }

 return b;

}

var c = a();

c();

這是個標準的閉包.在函數a中定義了函數b,a又return了b的值.這些可以先不管.

var c = a();

c();

這兩句執行很重要.

在var c = a();這行裏,執行了a函數,那麼肯定a經過了return.按照主流語言的函數特性,現在c的值就是a的返回值.第二行c()的執行實際執行的就是b函數.最後不管執行的是誰,會彈出一個值爲0的窗口,到此爲止,所有的生命週期按理論來說就算全部結束了.

可是,如果我們再多執行一行.var c = a();c();c();第一次彈出0,第二次執行卻彈出了1.也就是說,第一次c()後,a中的i依然保留.自然a在內存的棧區依然保留.a是return過了,但是,a及內部值卻依然存在,這就是閉包.

好了,總結下,

1,閉包外層是個函數.

2,閉包內部都有函數

.3,閉包會return內部函數.

4,閉包返回的函數內部不能有return.(因爲這樣就真的結束了)

5,執行閉包後,閉包內部變量會存在,而閉包內部函數的內部變量不會存在.

閉包的應用場景(呵呵,複製的參考資料)

1、保護函數內的變量安全。以最開始的例子爲例,函數a中i只有函數b才能訪問,而無法通過其他途徑訪問到,因此保護了i的安全性。

2、在內存中維持一個變量。依然如前例,由於閉包,函數a中i的一直存在於內存中,因此每次執行c(),都會給i自加1。

根據參考資料的應用場景,我們會自然的想到java或是c++的類.雖然JS沒有類的概念,但是有了類的相似執行結果.另外,還有一種格式頗受爭議:(function(a,b))(a,b);如果你使用過jquery,並且觀察過他的代碼,你就會很奇怪他的寫法,網上有人也把這種格式叫做閉包.

-------------------------------------------------------------------------------------------------------------------------

例1。 <script type="text/javascript">
function sayHello2(name) {
var text = 'Hello ' + name; // local variable
var sayAlert = function() { alert(text); }
return sayAlert;
}
var sy = sayHello2('never-online');
sy();
</script>
作爲一個Javascript程序員,應該明白上面的代碼就是一個函數的引用。如果你還不明白或者不清楚的話,請先了解一些基本的知識,我這裏不再敘述。

上面的代碼爲什麼是一個閉包
因爲sayHello2函數裏有一個內嵌匿名函數
sayAlert = function(){ alert(text); }
在Javascript裏。如果你創建了一個內嵌函數(如上例),也就是創建了一個閉包

在C 或者其它的主流語言中,當一個函數返回後,所有的局部變量將不可訪問,因爲它們所在的棧已經被消毀。但在Javascript裏,如果你聲明瞭一個內嵌函 數,局部變量將在函數返回後依然可訪問。比如上例中的變量sy,就是引用內嵌函數中的匿名函數function(){ alert(text); },可以把上例改成這樣:<script type="text/javascript">
function sayHello2(name) {
var text = 'Hello ' + name; // local variable
var sayAlert = function() { alert(text); }
return sayAlert;
}
var sy = sayHello2('never-online');
alert(sy.toString());
</script>這裏也就與閉包的第二個特點相吻合。

例2。<script type="text/javascript">
function say667() {
// Local variable that ends up within closure
var num = 666;
var sayAlert = function() { alert(num); }
num++;
return sayAlert;
}

var sy = say667();
sy();
alert(sy.toString());
</script>
上面的代碼中,匿名變量function() { alert(num); }中的num,並不是被拷貝,而是繼續引用外函數定義的局部變量??num中的值,直到外函數say667()返回。

例3。<script type="text/javascript">
function setupSomeGlobals() {
// Local variable that ends up within closure
var num = 666;
// Store some references to functions as global variables
gAlertNumber = function() { alert(num); }
gIncreaseNumber = function() { num++; }
gSetNumber = function(x) { num = x; }
}

</script>
<button οnclick="setupSomeGlobals()">生成 - setupSomeGlobals()</button>
<button οnclick="gAlertNumber()">輸出值 - gAlertNumber()</button>
<button οnclick="gIncreaseNumber()">增加 - gIncreaseNumber()</button>
<button οnclick="gSetNumber(5)">賦值5 - gSetNumber(5)</button>
上例中,gAlertNumber, gIncreaseNumber, gSetNumber都是同一個閉包的引用,setupSomeGlobals(),因爲他們聲明都是通過同一個全局調用??setupSomeGlobals()。
你可以通過“生成”,“增加”,“賦值”,“輸出值”這三個按扭來查看輸出結果。如果你點擊“生成”按鈕,將創建一個新閉包。也就會重寫gAlertNumber(), gIncreaseNumber(), gSetNumber(5)這三個函數。

如果理解以上代碼後,看下面的例子:

例4。<script type="text/javascript">
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push( function() {alert(item + ' ' + list[i])} );
}
return result;
}

function testList() {
var fnlist = buildList([1,2,3]);
// using j only to help prevent confusion - could use i
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}

testList();
</script>
運行結果:
item 3 is undefined
item 3 is undefined
item 3 is undefined

代碼result.push( function() {alert(item + ' ' + list[i])} ),
使result數組添加了三個匿名函數的引用。這句代碼也可以寫成
var p = function() {alert(item + ' ' + list[i])};
result.push(p);

關於爲什麼會輸出三次都是 "item 3 is undefined"

在上面的例子say667()例子中已經解釋過了。
匿 名函數function() {alert(item + ' ' + list[i])}中的list[i]並不是經過拷貝,而是對參數list的一個引用。直到函數buildList()返回爲止,也就是說,返回最後一個 引用。即遍歷完list(注:list的最大下標應該是2)後,經過i++也就變成了3,這也就是爲什麼是item 3,而list[3]本身是沒有初始化的,自然也就是undefined了。

例5。<script type="text/javascript">
function newClosure(someNum, someRef) {
// Local variables that end up within closure
var num = someNum;
var anArray = [1,2,3];
var ref = someRef;
return function(x) {
num += x;
anArray.push(num);
alert('num: ' + num +
'/nanArray ' + anArray.toString() +
'/nref.someVar ' + ref.someVar);
}
}
var closure1 = newClosure(40, {someVar:' never-online'})
var closure2 = newClosure(99, {someVar:' BlueDestiny'})
closure1(4)
closure2(3)
</script>


在這最後一個例子中,展示如何聲明兩個不同的閉包

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章