让人头大的闭包和垃圾回收机制GC

在理解了作用域和看了诸多如阮一峰大神的博客后之后,闭包还是花了很长时间去理解。

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。

1
2
3
4
5
6
7
8
9
function f1(){
var n = 999;
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result(); // 999 //实现了从函数外部读取函数内部变量n

什么是闭包?

上面代码中的f2函数,就是闭包。

  • 各种专业文献上的”闭包”(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数(f1)内部变量的函数(f2)。

  • 在js中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成”定义在一个函数(f1)内部的函数(f2)”。

  • 所以,在本质上,闭包就是将函数(f1)内部和函数外部(f1外部)连接起来的一座桥梁。在js中,所有的function都是一个闭包

闭包的用途 ## (#闭包的用途)

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,即维持一个变量。

闭包能够通过保护函数内的变量安全(即函数的变量只能由其内部函数访问,不能被其外部访问)实现JS私有属性和私有方法;

1
2
3
4
5
6
7
8
9
10
11
12
function f1(){
var n = 999;
nAdd = function () {n += 1};//全局变量,没加var
function f2(){
alert(n);
}
return f2;
}
var result = f1();//全局变量result = f2
result(); // 999
nAdd();
result(); // 1000
  • f2被赋予result,成为了全局变量,所以f2始终在内存中,而f2作用域链上依赖f1,所以导致f1也始终在内存中,在f1被引用后,不会被垃圾回收机制(garbage collection)回收;
  • var result=f1()这里引用了f1();n的值为999,并一直在内存中,运行result()即f2();结果999;nAdd()后,n=n+1=1000;再次执行result(),结果1000;

这就是这些函数内部的变量的值始终保持在内存中

使用闭包的注意点

  • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除

  • 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function outerFun() {
var a = 0;

function innerFun() {
a++;
alert(a);
}

return innerFun;
}

var obj = outerFun();
obj(); //1//当每次调用后,因为inner引用了outer的变量,inner(闭包)需要它们,所以这些变量的值还在内存中,不会被GC回收;
obj(); //2
// 不同引用之间变量的值不影响;
var obj2 = outerFun();
obj2(); //1
obj2(); //2

知道了这些,js的垃圾回收机制(GC)好懂多了! ## (#知道了这些js的垃圾回收机制gc好懂多了)

在Javascript中,

  • 如果一个对象不再被引用,那么这个对象就会被GC回收。

  • 如果两个对象互相引用,而不再被第3者所引用,那么,引用之后这两个互相引用的对象也会被回收。就像是:

1
2
3
4
5
6
7
8
9
function outer(){
var i=0;
function inner(){
i++;
}
inner();
}
//
这里面outer和inner相互引用,引用之后会被GC回收掉;
  • 如果函数a被b引用,b又被a外的c引用(闭包),则函数a执行后不会被回收的原因。

除此之外

把闭包讲的很深入的一篇博客 (target=undefined),还讲了函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。并以函数a从定义到执行的过程为例阐述这几个概念。