[关闭]
@linux1s1s 2015-04-26T08:21:39.000000Z 字数 3435 阅读 1361

JavaScript 闭包初步

JavaScript


内部类

我们先从一个基本的内部类开始

  1. function outerFn() {
  2. document.write("Outer function<br/>");
  3. function innerFn() {
  4. document.write("Inner function<br/>");
  5. }
  6. }

假如我们想在outerFn()外调用innerFn()肿么办

  1. function outerFn() {
  2. document.write("Outer function<br/>");
  3. function innerFn() {
  4. document.write("Inner function<br/>");
  5. }
  6. }
  7. innerFn();

上面的代码会出现JavaScript错误,那么该怎么调用呢?

伟大的逃逸

JavaScript允许开发人员像传递任何类型的数据一样传递函数,也就是说,JavaScript中的内部函数能够逃脱定义他们的外部函数。

逃逸方式一

  1. var globalVar;
  2. function outerFn() {
  3. document.write("Outer function<br/>");
  4. function innerFn() {
  5. document.write("Inner function<br/>");
  6. }
  7. globalVar = innerFn;
  8. }
  9. outerFn();
  10. globalVar();

这种逃逸方式是:内部函数指定给一个全局变量

逃逸方式二

  1. function outerFn() {
  2. document.write("Outer function<br/>");
  3. function innerFn() {
  4. document.write("Inner function<br/>");
  5. }
  6. return innerFn;
  7. }
  8. var fnRef = outerFn();
  9. fnRef();

这种逃逸方式是:通过在父函数的返回值来获得内部函数引用。

这种即使离开函数作用域的情况下仍然能够通过引用调用内部函数,意味着只要存在调用内部函数的可能,JavaScript就需要保留被引用的函数。而且JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间.

闭包特性

闭包是指有权限访问另一个函数作用域的变量的函数

闭包的特性:

  1. function outerFn() {
  2. document.write("Outer function<br/>");
  3. function innerFn() {
  4. var innerVar = 0;
  5. innerVar++;
  6. document.write("Inner function\t");
  7. document.write("innerVar = "+innerVar+"<br/>");
  8. }
  9. return innerFn;
  10. }
  11. var fnRef = outerFn();
  12. fnRef();
  13. fnRef();
  14. var fnRef2 = outerFn();
  15. fnRef2();
  16. fnRef2();

运行结果:

Outer function
Inner function innerVar = 1
Inner function innerVar = 1
Outer function
Inner function innerVar = 1
Inner function innerVar = 1

  1. var globalVar = 0;
  2. function outerFn() {
  3. document.write("Outer function<br/>");
  4. function innerFn() {
  5. globalVar++;
  6. document.write("Inner function\t");
  7. document.write("globalVar = " + globalVar + "<br/>");
  8. }
  9. return innerFn;
  10. }
  11. var fnRef = outerFn();
  12. fnRef();
  13. fnRef();
  14. var fnRef2 = outerFn();
  15. fnRef2();
  16. fnRef2();

运行结果:

Outer function
Inner function globalVar = 1
Inner function globalVar = 2
Outer function
Inner function globalVar = 3
Inner function globalVar = 4

我们看到的是前面两种情况合成的效果,通过每个引用调用innerFn都会独立的递增outerVar。也就是说第二次调用outerFn没有继续沿用outerVar的值,而是在第二次函数调用的作用域创建并绑定了一个一个新的outerVar实例,两个计数器完全无关。

闭包的交互

  1. function outerFn() {
  2. var outerVar = 0;
  3. document.write("Outer function<br/>");
  4. function innerFn1() {
  5. outerVar++;
  6. document.write("Inner function 1\t");
  7. document.write("outerVar = " + outerVar + "<br/>");
  8. }
  9. function innerFn2() {
  10. outerVar += 2;
  11. document.write("Inner function 2\t");
  12. document.write("outerVar = " + outerVar + "<br/>");
  13. }
  14. return { "fn1": innerFn1, "fn2": innerFn2 };
  15. }
  16. var fnRef = outerFn();
  17. fnRef.fn1();
  18. fnRef.fn2();
  19. fnRef.fn1();
  20. var fnRef2 = outerFn();
  21. fnRef2.fn1();
  22. fnRef2.fn2();
  23. fnRef2.fn1();

运行结果:

Outer function
Inner function 1 outerVar = 1
Inner function 2 outerVar = 3
Inner function 1 outerVar = 4
Outer function
Inner function 1 outerVar = 1
Inner function 2 outerVar = 3
Inner function 1 outerVar = 4

innerFn1和innerFn2引用了同一个局部变量,因此他们共享一个封闭环境。当innerFn1为outerVar递增一时,就为innerFn2设置了outerVar的新的起点值,反之亦然。我们也看到对outerFn的后续调用还会创建这些闭包的新实例,同时也会创建新的封闭环境,本质上是创建了一个新对象,自由变量就是这个对象的实例变量,而闭包就是这个对象的实例方法,而且这些变量也是私有的,因为不能在封装它们的作用域外部直接引用这些变量,从而确保了了面向对象数据的专有性。

看完上面的基本知识,你或许对闭包有了一个比较浅显的认识,学过C/C++的同学应该理解起来比较容易,因为闭包特别像是指针函数,但是有不是完全是,因为还有作用域的概念。如果想更深入的认识闭包可以进一步学习JavaScript作用域链和活动对象。

本文转自:http://www.cnblogs.com/dolphinX/archive/2012/09/29/2708763.html 部分内容做了修改,特此说明。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注