書接上回,繼續閉包。

Closures

1、自動的垃圾回收
?? ECMAScript有自動的垃圾回收機制。與java類似。但是規范也沒有對該機制詳細定義,而是讓瀏覽器等規范實現廠家來實現,各種瀏覽器實現不一樣,垃圾回收的算法也不同。好象ie的實現會出現內存溢出問題。對我我們來說注意到這點就夠了,后面會提到如何避免ie的這個bug.
?? 關于上篇提到的execution context,調用對象,參數,scope chain 等等都需要內存,垃圾回收機制會在適當時候釋放內存。
2、閉包如何形成
?? 通俗的說,當一個(outer)函數的返回類型是(inner)函數類型時,這個被返回的inner函數斥又outer函數的scope chain,這時候閉包形成。? 如下例:

function exampleClosureForm(arg1, arg2){
??? var localVar = 8;
??? function exampleReturned(innerArg){
??????? return ((arg1 + arg2)/(innerArg + localVar));
??? }
??? /* return a reference to the inner function defined as -
?????? exampleReturned -:-
??? */
??? return exampleReturned;
}
var globalVar = exampleClosureForm(2, 4);

? 現在exampleClosureForm(2, 4)返回的inner函數不能被垃圾回收,因為它被變量globalVar持有,并可執行globalVar(n)。
? 但是內部的原理沒有表面上那么簡單。現在globalVar是個函數對象,它的[[scope]] property 指向一個scope chain,而這個scope chain 包括?? exampleClosureForm函數的調用對象+global對象。所以垃圾回收不能回收這部分內存。
? 一個閉包形成了。inner函數對象有自己的變量,也可以訪問exampleClosureForm函數調用過程中的參數,local變量等。
? 在上面的例子中,globalVar(n)執行時,在通過調用對象可以訪問到exampleClosureForm(2, 4)執行過程中的參數,local變量等。arg1 = 2,arg2 = 4 ,localVar=8,這些屬性都通過調用對象"ActOuter1"可以得到。

如果增加以下代碼,又返回另外一個inner 函數。
var secondGlobalVar = exampleClosureForm(12, 3);
exampleClosureForm(12, 3)會引起新的調用對象創建,我們定義為ActOuter2。這個過程中,arg1 = 12,arg2 = 3 ,localVar=8。第二個閉包形成了.

?? 下面考慮返回的inner函數執行過程。如globalVar(2)。新的execution context、調用對象(ActInner)被創建。現在的scope chain是? ActInner1->ActOuter1->global object.? 函數返回是 ((2 + 4)/(2 + 8)).
??? 如果是secondGlobalVar(5)被執行情況是什么呢?現在的scope chain是ActInner2-> ActOuter2-> global object.函數返回是 ((12 + 3)/(5 + 8)).

??? 通過比較,這兩個inner函數互不干擾的執行。如果嵌套更多的函數的話,與上面所訴類似。明白的javascript的閉包,從這個方面可能就能體會到它比java等慢n個數量級的原因。
3、閉包能做什么(例子)
(1)
function callLater(paramA, paramB, paramC){
??? return (function(){
??????? paramA[paramB] = paramC;
??? });
}
var functRef = callLater(elStyle, "display", "none");
hideMenu=setTimeout(functRef, 500);
想象我們做顏色漸變,或者動畫的時候吧。上面提供的函數多幽雅。
(2)
function associateObjWithEvent(obj, methodName){
??? return (function(e){
??????? e = e||window.event;
??????? return obj[methodName](e, this);
??? });
}
function DhtmlObject(elementId){
??? var el = getElementWithId(elementId);
??? if(el){
??????? el.onclick = associateObjWithEvent(this, "doOnClick");
??????? el.onmouseover = associateObjWithEvent(this, "doMouseOver");
??????? el.onmouseout = associateObjWithEvent(this, "doMouseOut");
??? }
}
DhtmlObject.prototype.doOnClick = function(event, element){
??? ... // doOnClick method body.
}
DhtmlObject.prototype.doMouseOver = function(event, element){
??? ... // doMouseOver method body.
}
DhtmlObject.prototype.doMouseOut = function(event, element){
??? ... // doMouseOut method body.
}
......


又一種注冊事件的方法。我覺得作者的這種實現可謂精妙。大大的開闊了我的思路。我們可以為我們的UI事件綁定到對象上,可以很好的重用代碼。另外比起prototype.js的時間注冊來說簡單點。
(3)
var getImgInPositionedDivHtml = (function(){
??? var buffAr = [
??????? '<div id="',
??????? '',?? //index 1, DIV ID attribute
??????? '" style="position:absolute;top:',
??????? '',?? //index 3, DIV top position
??????? 'px;left:',
??????? '',?? //index 5, DIV left position
??????? 'px;width:',
??????? '',?? //index 7, DIV width
??????? 'px;height:',
??????? '',?? //index 9, DIV height
??????? 'px;overflow:hidden;\"><img src=\"',
??????? '',?? //index 11, IMG URL
??????? '\" width=\"',
??????? '',?? //index 13, IMG width
??????? '\" height=\"',
??????? '',?? //index 15, IMG height
??????? '\" alt=\"',
??????? '',?? //index 17, IMG alt text
??????? '\"><\/div>'
??? ];
??? return (function(url, id, width, height, top, left, altText){
??????? buffAr[1] = id;
??????? buffAr[3] = top;
??????? buffAr[5] = left;
??????? buffAr[13] = (buffAr[7] = width);
??????? buffAr[15] = (buffAr[9] = height);
??????? buffAr[11] = url;
??????? buffAr[17] = altText;
??????? return buffAr.join('');
??? }); //:End of inner function expression.
})();

這種匿名函數的調用在dojo中見過,現在再看,感覺不一樣。

以上是原作者的例子,我抄過來的。下次我準備深入研究一下閉包能給我們開發js類庫提供什么更好的思路。感覺現在很多人對閉包了解不多,經過這段時間的思考,利用javascript中的閉包,代碼偶合性會更低。