Ji Xiang's blog

JavaScript学习笔记(3)

JavaScript Closure

学习JavaScript,不能不知道闭包(closure)的概念和作用。闭包是JavaScript中重要的一个概念,对于初学者来讲,闭包很难理解和运用。但只有理解了闭包的基本概念,才能正确的使用它;同样的,只有配合正确的实践,我们才有更加深刻的理解。因此,这片文章借鉴了很多前人的总结和经验,只为更好的理解闭包。

闭包的概念

对于码农来说,学习一门新技术,除了看官方API和相关文档,示例代码的对我们的帮助可能更大,因为代码的运行结果更加直观,能加深我们的理解。下面贴出大牛的代码,号称世上最简单的闭包:

1
2
3
4
5
6
7
8
function A(){
function B() {
console.log('Hello closure!');
}
return B;
}
var c = A();
// c(); Hello closure!

确实,这个示例代码真的不能再简单了。这段代码“翻译”后可以表示为:

  1. 定义了一个函数A
  2. 在A中定义了内部函数B
  3. 在A中返回B(确切的讲,在A中返回B的引用)
  4. 执行A(),把A的返回值-函数B()的引用复制给变量c
  5. 执行 函数B(),输出结果

根据上述例子,闭包可定义为:
当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包。

闭包的作用

在了解闭包的作用之前,我们先了解一下JavaScript中的垃圾回收(GC)机制:在JavaScript中,如果一个对象不再被引用,那么这个对象就会被GC回收,否则这个对象会一直保存在内存中。
在上述例子中,B定义在A中,因此B依赖于A,而外部变量c获得了内部函数B的引用,也就是说,A被间接引用了,所以A不会被GC回收,而是一直保持上下文,在同一个引用调用结束前,会一直保存在内存中,直到调用结束。
示例2:

1
2
3
4
5
6
7
8
9
10
11
12
function A() {
var count = 0;
function B() {
count++;
console.log(count);
}
return B;
}
var c = A();
c();//1
c();//2
c();//3

count是A中的一个变量,它的值在B中被改变,函数B每执行一次,count的值在原来的基础上累加1,A被变量c引用,不会被GC,因此,A中的count一直保存在内存中,c每执行一次,由于保留了上下文,count的值会在原来的基础上增加1。
这就是闭包的作用,有时候我们需要一个模块中定义这样一个变量:希望这个变量一直保存在内存中,但又不会“污染”全局的变量,这个时候,我们就可以用闭包来定义这个模块。

结语

上面的例子只是最简陋的写法,实际中不会这么写。这里只是为了加深理解。当然,如果要全面理解闭包,仅凭上面的文字和代码是远远不够的。在项目中不妨试着使用闭包,用的多了自然会有独到的理解。