先把GC和Memory Leak探究暫時放下,牽涉的東西較多。需要逐一進(jìn)行解釋。
比如這篇文章就是其中的一個影子——閉包(closure)。
特別注明:我的翻譯是很爛的...:(,而且并沒有按原文那樣一路譯下來,我只是抽取70%左右并加了一些自己的注釋。
所以建議能讀懂英文的朋友,可以點擊最下面的鏈接去讀原文。
閉包的兩個特點:
1。作為一個函數(shù)變量的一個引用 - 當(dāng)函數(shù)返回時,其處于激活狀態(tài)。
2。一個閉包就是當(dāng)一個函數(shù)返回時,一個沒有釋放資源的棧區(qū)。
例1。
作為一個Javascript程序員,應(yīng)該明白上面的代碼就是一個函數(shù)的引用。如果你還不明白或者不清楚的話,請先了解一些基本的知識,我這里不再敘述。
上面的代碼為什么是一個閉包?
因為sayHello2函數(shù)里有一個內(nèi)嵌匿名函數(shù)
sayAlert = function(){ alert(text); }
在Javascript里。如果你創(chuàng)建了一個內(nèi)嵌函數(shù)(如上例),也就是創(chuàng)建了一個閉包。
在C或者其它的主流語言中,當(dāng)一個函數(shù)返回后,所有的局部變量將不可訪問,因為它們所在的棧已經(jīng)被消毀。但在Javascript里,如果你聲明了一個內(nèi)嵌函數(shù),局部變量將在函數(shù)返回后依然可訪問。比如上例中的變量sy,就是引用內(nèi)嵌函數(shù)中的匿名函數(shù)function(){ alert(text); },可以把上例改成這樣:
例2。
上面的代碼中,匿名變量function() { alert(num); }中的num,并不是被拷貝,而是繼續(xù)引用外函數(shù)定義的局部變量——num中的值,直到外函數(shù)say667()返回。
例3。
上例中,gAlertNumber, gIncreaseNumber, gSetNumber都是同一個閉包的引用,setupSomeGlobals(),因為他們聲明都是通過同一個全局調(diào)用——setupSomeGlobals()。
你可以通過“生成”,“增加”,“賦值”,“輸出值”這三個按扭來查看輸出結(jié)果。如果你點擊“生成”按鈕,將創(chuàng)建一個新閉包。也就會重寫gAlertNumber(), gIncreaseNumber(), gSetNumber(5)這三個函數(shù)。
如果理解以上代碼后,看下面的例子:
例4。
item 3 is undefined
item 3 is undefined
item 3 is undefined
代碼result.push( function() {alert(item + ' ' + list[i])} ),
使result數(shù)組添加了三個匿名函數(shù)的引用。這句代碼也可以寫成
var p = function() {alert(item + ' ' + list[i])};
result.push(p);
關(guān)于為什么會輸出三次都是 "item 3 is undefined"
在上面的例子say667()例子中已經(jīng)解釋過了。
匿名函數(shù)function() {alert(item + ' ' + list[i])}中的list[i]并不是經(jīng)過拷貝,而是對參數(shù)list的一個引用。直到函數(shù)buildList()返回為止,也就是說,返回最后一個引用。即遍歷完list(注:list的最大下標(biāo)應(yīng)該是2)后,經(jīng)過i++也就變成了3,這也就是為什么是item 3,而list[3]本身是沒有初始化的,自然也就是undefined了。
例5。
在這最后一個例子中,展示如何聲明兩個不同的閉包。
比如這篇文章就是其中的一個影子——閉包(closure)。
特別注明:我的翻譯是很爛的...:(,而且并沒有按原文那樣一路譯下來,我只是抽取70%左右并加了一些自己的注釋。
所以建議能讀懂英文的朋友,可以點擊最下面的鏈接去讀原文。
閉包的兩個特點:
1。作為一個函數(shù)變量的一個引用 - 當(dāng)函數(shù)返回時,其處于激活狀態(tài)。
2。一個閉包就是當(dāng)一個函數(shù)返回時,一個沒有釋放資源的棧區(qū)。
例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>
function sayHello2(name) {
?var text = 'Hello ' + name; // local variable
?var sayAlert = function() { alert(text); }
?return sayAlert;
}
var sy = sayHello2('never-online');
sy();
</script>
作為一個Javascript程序員,應(yīng)該明白上面的代碼就是一個函數(shù)的引用。如果你還不明白或者不清楚的話,請先了解一些基本的知識,我這里不再敘述。
上面的代碼為什么是一個閉包?
因為sayHello2函數(shù)里有一個內(nèi)嵌匿名函數(shù)
sayAlert = function(){ alert(text); }
在Javascript里。如果你創(chuàng)建了一個內(nèi)嵌函數(shù)(如上例),也就是創(chuàng)建了一個閉包。
在C或者其它的主流語言中,當(dāng)一個函數(shù)返回后,所有的局部變量將不可訪問,因為它們所在的棧已經(jīng)被消毀。但在Javascript里,如果你聲明了一個內(nèi)嵌函數(shù),局部變量將在函數(shù)返回后依然可訪問。比如上例中的變量sy,就是引用內(nèi)嵌函數(shù)中的匿名函數(shù)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>
這里也就與閉包的第二個特點相吻合。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 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,并不是被拷貝,而是繼續(xù)引用外函數(shù)定義的局部變量——num中的值,直到外函數(shù)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 onclick="setupSomeGlobals()">生成 - setupSomeGlobals()</button>
<button onclick="gAlertNumber()">輸出值 - gAlertNumber()</button>
<button onclick="gIncreaseNumber()">增加 - gIncreaseNumber()</button>
<button onclick="gSetNumber(5)">賦值5 - gSetNumber(5)</button>
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 onclick="setupSomeGlobals()">生成 - setupSomeGlobals()</button>
<button onclick="gAlertNumber()">輸出值 - gAlertNumber()</button>
<button onclick="gIncreaseNumber()">增加 - gIncreaseNumber()</button>
<button onclick="gSetNumber(5)">賦值5 - gSetNumber(5)</button>
上例中,gAlertNumber, gIncreaseNumber, gSetNumber都是同一個閉包的引用,setupSomeGlobals(),因為他們聲明都是通過同一個全局調(diào)用——setupSomeGlobals()。
你可以通過“生成”,“增加”,“賦值”,“輸出值”這三個按扭來查看輸出結(jié)果。如果你點擊“生成”按鈕,將創(chuàng)建一個新閉包。也就會重寫gAlertNumber(), gIncreaseNumber(), gSetNumber(5)這三個函數(shù)。
如果理解以上代碼后,看下面的例子:
例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>
運行結(jié)果: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數(shù)組添加了三個匿名函數(shù)的引用。這句代碼也可以寫成
var p = function() {alert(item + ' ' + list[i])};
result.push(p);
關(guān)于為什么會輸出三次都是 "item 3 is undefined"
在上面的例子say667()例子中已經(jīng)解釋過了。
匿名函數(shù)function() {alert(item + ' ' + list[i])}中的list[i]并不是經(jīng)過拷貝,而是對參數(shù)list的一個引用。直到函數(shù)buildList()返回為止,也就是說,返回最后一個引用。即遍歷完list(注:list的最大下標(biāo)應(yīng)該是2)后,經(jīng)過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>
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>
在這最后一個例子中,展示如何聲明兩個不同的閉包。