javascript闭包
来源:互联网 发布:淘宝淘客推广 编辑:程序博客网 时间:2024/06/11 06:18
【原文】http://www.mollypages.org/misc/jsclo.mp
1.好文章
略.
2. 说明:嵌套函数
- 嵌套函数就是定义在另一个函数内的函数。
§
§ functionfoo()
§ {
§ function bar() { }
§ }
函数bar是无法从函数foo外部访问,比如像这样使用:foo.bar()
- 内部函数能够被暴露到外部函数外面。
§
§ functionfoo()
§ {
§ bar = function () { }
§ }
§
§ foo(); //=> assigns the inner (anonymous) function to global variable bar
§ bar(); //=>invokes nested function bar
函数bar现在能够从函数foo外部访问,因为它被赋值给了一个全局变量bar(由于bar没有用var声明,所以它成了一个全局变量)。
注意,foo()至少被调用一次,这样变量bar才能够被赋值。
- 内部函数也可以从外部函数返回。
§
§ functionfoo()
§ {
§ var bar = function () { }
§ return bar;
§ }
§
§ varbar_reference = foo();
§ bar_reference();//=>invokes nested function bar
函数bar现在能够在函数foo外部访问到,因为它被赋值给一个全局变量bar_reference。
注意,foo()至少被调用一次来返回bar的引用。
3. 闭包
- 正如上面所讲,嵌套函数能在外部函数之外被访问。闭包是在定义嵌套函数和return的时候被创建(或定义和调用)。
闭包简单来说就是内部函数能够访问外部函数内的变量(局部变量)。
很有趣的地方是,在外部函数已经执行完毕并不再存在,它还能够访问外部函数的那些局部变量。这是通过javascript内部把本地外部变量的状态保存在一个隔离的闭包变量中。
function foo()
{
var x = "hello";
function bar() {
alert(x);
}
return bar;
}
var bar_ref = foo();
//foo has finished executing at this point and local
//variable x does not exist anymore
bar_ref(); //=> alerts "hello"
在这个例子中,bar_ref() 能正确地弹出”hello”。
因为变量x仍能通过闭包访问到。
- 局部变量是通过在外部函数中利用关键字var和任何的方法参数保存在闭包中的。
·
· functionfoo(x)
· {
· function bar() {
· alert(x);
· }
· return bar;
· }
·
· varbar_ref = foo("hello");
· bar_ref();
在这个例子中,bar_ref() 能正确地弹出"hello",因为局部方法的形式参数能够通过闭包被访问到。
- 重要说明:在内部函数被赋值或返回的任何时候都会创建一个新的闭包。
function foo(x)
{
function bar() {
alert(x);
}
return bar;
}
var bar_ref_1 = foo("hello");
var bar_ref_2 = foo("world");
bar_ref_1(); //alerts hello
bar_ref_2(); //alerts world
说明:这意味通过同样的嵌套函数能创建多个闭包(每次嵌套函数被调用的时候创建新闭包),另外,修改一个闭包的变量不会影响另一个闭包的变量(译者注:闭包变量相互对立)。
- 4. 闭包什么时候有用呢 ?
闭包基本上让我们摆脱了创建全局变量的需求。当事件/回调函数需要回调一些特定的数据时,闭包会发挥很大的作用。
在Java中,我们可以定义一个所谓叫回调函数的方法。我们能够创建多个不同的对象(每个都在不同的实例数据里),并且可以指定一个会被回调方法调用的特定对象。这种方法,在被调用时,将能够访问它自己的数据对象。
class MyObject {
String greeting;
MyObject(String word) {
this.greeting = word;
}
void showGreeting() { System.out.print(greeting); }
}
MyObject obj1 = new MyObject("hello");
MyObject obj2 = new MyObject("world");
SomeEventHandler.addCallBack(obj1); //invokes showGreeting() and prints "hello"
SomeEventHandler2.addCallBack(obj2); //invokes showGreeting() and prints "world"
说明,事件句柄是在不同的线程上运行的(通常是awt/swing事件句柄线程)但被调用的方法能够访问它自身的数据对象(因为所有的方法都跟对象绑定在一起)。
换个说法,(在Java里),它通常传递一个"this"对象指针给一个事件句柄(当一个事件句柄被对象添加到它自身时),这样事件句柄就能够使用"this"指针方法那个特定对象的数据。
传递给事件句柄的对象通常实现了一个接口,这样事件句柄(和编译器)就能够知道该对象具有一个众所周知的回调方法。(在这个例子中的showGreeting())
相比之下,现在在Javascript中,开始流行这个:
var greeting = "hello";
function foo() {
alert(greeting);
}
SomeElement.addEventHandler("onclick", foo);
SomeElement2.addEventHandler("onclick", foo);
事件句柄将调用全局函数(仅能够访问全局变量)。在这里例子,我们仅能使用一个greeting变量,每次它都是从foo()读取(因为它是一个全局变量)。
如果我们现在试着按照Java的模型和对象化(object-ify),我们可以说:
function MyObject(word) {
this.greeting = word;
this.handle = function(){
alert(this.greeting);
}
}
var obj1 = new MyObject("hello");
var obj2 = new MyObject("world");
SomeElement.addEventHandler("onclick", obj1);
SomeElement2.addEventHandler("onclick", obj2);
这可能有能行,或不行。这取决于它是怎样实现回调的:
回调机制能把回调方法作为一个函数来调用。如果嵌套调用 obj1.handle()这个事件句柄,那’this’将不能指向正确的对象(它将指向全局变量即window,而不是obj1)
这种回调方式甚至可能不能接收一个对象,它可能仅仅能接收一个函数。这个在javascript中非常普遍。例如,setTimeout只能接受一个函数。
1.SomeElement.addEventHandler("onclick",obj1, some_function);
在这里情况,访问一个函数中的特定数据将是不能(除非是一个全局变量或一个闭包)。
那闭包是怎么解决这问题呢?
function remember_me(word) {
function inner() {
alert(word);
}
return inner;
}
SomeElement.addEventHandler("onclick", remember_me("hello"));
在这个例子,函数和相关的数据一起被记录在一个闭包中。事件句柄使用一个函数做回调函数(不是一个对象)。然而,这个函数能够访问它自身独立的数据,每个闭包函数都有其单独的数据。
在概念上跟有一个基于回调访问自身对象数据的方法相似。
我感觉闭包一般趋向于让事情变得更复杂和难以理解。它们不该是一种编程语言的特性,仅是困惑的源头(在javascript,基于上面的一些原因闭包有时是必要的)。
5. 浏览器特定的hacks和bugs
IE有一个垃圾回收bugs。这些不是闭包特有的,但在使用闭包的时候很容易会暴露。
正如上面的图所展示的,IE(7和更早版本)分开回收本地javascript和映射为javascript对象的HTML DOM的垃圾。
如果在HTML DOM对象和一个本地对象有一个环形链接,然后在IE浏览器的会话周期中都不会被作为垃圾回收(即使用从页面跳转到别的地方)。
function addHandler()
{
var mydiv =document.getElementById("myid");
mydiv.onclick = function() {
alert(this.innerHTML +"\n");
}
在这个例子,mydiv通过它的onclick事件句柄指向本地JS代码。
事件句柄的js代码通过内部产生的闭包重指向回mydiv 。这个环形引用导致了在IE中的内存泄露。
解决此问题的方法:
function addHandler()
{
var mydiv =document.getElementById("myid");
mydiv.onclick = handler;
}
function handler() {
alert(this.innerHTML +"\n");
}
因为handler函数不是一个嵌套函数,所以没有闭包产生。
另一个中没有闭包的解决方法:
function addHandler()
{
var mydiv =document.getElementById("myid");
mydiv.onclick = new Function(
'alert(this.innerHTML)'
);
}
尽管句柄函数是被内联定义,但函数构造函数的使用阻止了一个隐式的闭包(通过构造函数创建的函数不会保存局部作用域)。
译者注:初次翻译,翻译得不对或不完善的,欢迎拍砖。
- 【JavaScript】Javascript闭包
- javascript的闭包javascript
- Javascript闭包演示javascript
- [ javascript ] javascript闭包测试!
- 【javascript】javascript中的闭包
- 【javascript】学习Javascript闭包
- 理解 JavaScript 闭包
- javascript 闭包
- javascript闭包
- Javascript闭包
- JavaScript中的"闭包"
- JavaScript 闭包
- javascript中的闭包
- 理解 JavaScript 闭包
- Javascript Closures[闭包]
- javascript闭包
- Javascript 闭包详解
- Javascript闭包技术
- 第一周作业——编写计算“两个整数的最大公约数”程序
- 音乐播放器3
- 取postgresql总sequence得值
- hdu1673 Optimal Parking
- 容灾系统
- javascript闭包
- 关于Android Canvas.drawText方法中的坐标参数的正确解释
- level set method 水平集方法基本问题
- 其i是 世界在变
- Android开发之旅一常用的正则表达式
- (译)如何优化cocos2d程序的内存使用和程序大小:第二部分(完)
- S3C6410 SPI全双工读写流程分析(原创)
- VelocityTracker简介
- linux静态共享库实验