书接
上回,继续闭包。
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中的闭包,代码偶合性会更低。