关于JavaScript闭包的理解
Liber 2012-10-09 10:56

先看一个例子:

//第一段程序
var name = "The Window";
var object = {
	name : "My Object",
	getNameFunc : function(){
	var that = this;
	return function(){
			return that.name;
		};
	}
};
alert(object.getNameFunc()());
 
//第二段程序
var name = "The Window";
var object = {
	name : "My Object",
	getNameFunc : function(){
	return function(){
			return this.name;
		};
	}
};
alert(object.getNameFunc()());

这里有几个知识点要说明一下:

this:在Javascript中,this关键字永远都指向函数(方法)的所有者。(参见:http://www.laruence.com/2009/09/08/1076.html

定义和执行:函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定。(参见:http://www.felixwoo.com/archives/247

在第一段程序中,函数getNameFunc的所有者是object对象,所以在函数getNameFunc的里面的this指向object,此时把this赋值给that,所以that中也是object对象,所以return的时候可以获得object对象的属性name的值“My Object”;(return的是一个在object的getNameFunc函数中定义的一个匿名函数,相当于在getNameFunc函数里的那个匿名函数在getNameFunc函数外被引用,这样就产生了闭包)

在第二段程序中,this出现在一个匿名函数当中,当我们去找该匿名函数的所有者的时候,找到了getNameFunc函数,而此时遇到了getNameFunc的return,该return返回给alert函数,所以该匿名函数的所有者变成了Window对象,所以最后输出Window对象的属性“The Window”。

这里引出闭包的便于理解的概念:

function a() {
    var i = 0;
    function b() {
        alert(++i);
    }
    return b;
}
var c = a();
c();

当函数a的内部函数b被函数a外的一个变量引用的时候,就创建了一个我们通常所谓的“闭包”。

再看一个例子:

//第一段程序
for (var i=0; i<5; i++) {
    $('<p>click me</p>').appendTo('body').click(function() {
        alert(i);
    });
}
 
 //第二段程序
var createFunction = function(i) {
    return function() { alert(i); };
};
for (var i=0; i<5; i++) {
    $('<p>click me</p>').appendTo('body').click(createFunction(i));
}

在这里,第一段程序循环体里面,每次都会使用i的值,但是实际上,i的值是在变化的,所以,而程序会检测到这个值的变化,所以,点击任意一个按钮都会显示4;

第二段程序,循环体当中绑定了一个函数,这个函数接受i的值,最后又返回一个匿名函数,实际上就是,在循环体内的函数引用了createFunction函数内部的一个匿名函数,这就产生了一个闭包,所以i的值不会被之后的改变影响,从而输出想要的结果。

以上就是我对闭包的理解,如果有什么不对,非常欢迎大家指正。

补充例子:(代码引自http://blog.csdn.net/natineprince/article/details/4759533

var abc = function(y) {
    var x = y; // 这个是局部变量  
    return function() {
        alert(x++); // 就是这里调用了闭包特性中的一级函数局部变量的x,并对它进行操作  
        alert(y--); // 引用的参数变量也是自由变量  
    }
} (5); // 初始化  
abc();
abc();
abc();
alert(x);

自己先想一想输出结果是什么,然后在浏览器的控制台运行代码看看结果,一定要理解清楚。

有个知识点要注意一下:函数名仅仅只是指向函数的指针,也就是说,一个函数可能会有多个名字。