Ajax In Action 附录B 3.5

来源:互联网 发布:c语言位运算符号 编辑:程序博客网 时间:2024/05/17 03:02

B3.5Closures in JavaScript

对于函数对象自己来讲,他还没有完成,为了引用它,我们需要传一个上下文对象和一系列的参数,有可能这些参数都是空的。最简单的,closure可以被看成是处理需要执行的资源的函数。

Closure是在JavaScript中隐式地创建的,不是显式。没有new Closure()这种构造函数,也没有办法可以控制一个closure对象。创建closure就像在代码块中声明一个函数一样简单,并让这个函数在声明块之外也能被访问。这个概念看起来有点奇怪,不过举个例子就很明白了。我们来定义一个简单的创建机器人的对象,并且能纪录机器人是什么时候被创建的。我们可以这么写:

function Robot(){

var createTime=new Date();

this.getAge=function(){

var now=new Date();

var age=now-createTime;

return age;

}

}

因为所有的机器人都是一样的,我们就没有必要通过构造函数来指定名字之类的东西。通常来说,我们把createTime作为一个属性,也就是:

this.createTime=new Date();

但是在这里我们故意把它作为一个局部变量,这个局部变量的作用域就是调用他的块的内部,也就是,我们的构造函数。在构造函数的第二行,我们定义了一个getAge()函数,这个函数是我们定义在另一个函数内部的,而且这个内部函数使用了createTime这个局部变量,本来createTime的作用域是外层的函数。这样,其实我们就完成了closure的创建。如果在页面装载的时候,定义一个机器人,并且问他多大了,

var robbie=new Robot();

window.onload=function(){

alert(robbie.getAge());

}

正常情况下,应该给我们返回一个10-50毫秒的值,差别就在脚本第一次执行和页面装载。虽然我们在构造函数作用域内把createTime定义成局部变量,但是只要机器人还在被引用,createTime占用的内存就不会被回收,因为他还属于一个closure

只有在内部函数在外层函数的定义体内创建的时候才有用,如果我们重新写getAge()函数,像这样:

function Robot(){

var createTime=new Date();

this.getAge=roboAge;

}

function roboAge(){

var now=new Date();

var age=now-createTime;

return age;

};

这样closure就不会被创建,而且会得到一个createTime没有定义的错误。

Closure很容易被创建,而且是太容易被创建了,因为它总是将一些局部变量绑定,阻止垃圾回收机制回收它们。如果DOM的结点,通过这种方式创建,将会导致内存溢出。

创建closure的最普遍的情况就是在绑定事件处理器到事件的时候,像我们在B3.4提到过的一样,被上下文应用的回调函数往往不是很好用。我们为多余的引用定义一些模式,允许我们从DOM元素来检索模型。Closure为我们提供了可选择的方法,如下:

myObj.prototype.createView=function(){

...

this.titleBar=document.createElement("div");

var modelObj=this;

this.titleBar.onclick=function(){

fooEventHandler.call(modelObj);

}

}

我们定义的匿名的onclick处理器函数引用了局部变量modelObj,所以一个closure就被创建了,函数被引用的时候,modelObj也就可用了。注意closure只负责局部变量,而不是那些通过this关键字引用的变量。

我们在第二章的时候在ContentLoder对象用到了这种方法,因为IE浏览器提供的onreadystatechange回调将窗口作为函数的上下文返回。因为window是全局对象,我们就没有办法知道哪个ContentLoaderreadySatate改变了,除非我们通过closure给相关的装载对象传递一个引用。

我推荐普通得Ajax程序员尽量避免closure,如果你使用原型来给你自定义的类型定义函数,那么你没有复制函数,也没有创建closure。让我们根据这个建议来重写一下Robot类:

function Robot(){

this.createTime=new Date();

}

Robot.prototype.getAge=function(){

var now=new Date();

var age=now-this.createTime;

return age;

};

getAge()函数只定义了一次,因为它是定义在原型上的,在每创建一个Robot的时候都是可以访问的。

Closure有他们的用处,但是我们得把他们看成高级课题,如果你想进一步学习closure,那么资源部分的Jim Ley的文章,作为一个入门还是不错的。