javascript闭包

227 阅读5分钟

一、书上的闭包概念

关于javascript闭包,首先我们来看一些书籍中所给的定义。

《javaScript权威指南》中的定义如下:

函数对象可以通过作用域链互相关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学中成为闭包。

《javaScript高级教程》中的定义如下:

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

二、变量的作用域

由此可见,想要理解闭包,我们首先必须理解javaScript变量的作用域。

而变量的作用域无非就是2种:全局变量和局部变量

所谓的全局作用域就是函数外部的区域,而全局变量就是在函数外部所定义的变量。

如下代码,在函数(f1)内部可直接读取全局变量(age)。

 //此处为全局作用域
  
   var age=18;// 此为全局变量age
   
  function f1(){// 此为函数f1
    alert(age);
  }

  f1(); // 18

所谓的局部作用域就是函数内部的区域,而局部变量就是在函数内部所定义的变量。

如下代码,在函数(f1)外部无法读取函数内部的变量(age)。

function f1(){// 此为函数f1
    	// 此处为局部作用域
        
    	var age=18;// 此为局部变量age
  }
  
alert(age);// error

注意:在定义局部变量的时候,一定要使用var命令,否则实际上声明的是一个全局变量。

三、如何从函数外部读取局部变量

以上例子证明了2点,作用域决定了这些变量的可访问性(可见性)。函数内部定义的变量从函数外部是不可访问的(不可见的)。但是往往出于某些原因,我们有时候需要得到局部变量。那么如何实现呢,请继续往下看。

其实很简单,我们可以在函数的内部,再定义一个函数。

function f1(){
     var age=18;
         
     function f2(){
         alert(age);// 18
     }
}

以上例子中,可以看见函数f2是定义在函数f1内部的,这也就代表在函数f2中可以访问函数f1内的所有局部变量。但是反过来在函数f1中是无法访问到函数f2中的局部变量的。这种结构就是javascript语言特有的“链式作用域”,子对象会一级一级地向上寻找所有父对象的变量。也就是,父对象的所有变量,对子对象都是可见的,反之则不成立。

而由于函数f2能访问到函数f1中的局部变量,那么只需要将函数f2作为返回值,我们就可以在函数f1外部读取到它的局部变量了。

    function f1(){
         var age=18;
         
         function f2(){// 此函数f2就是闭包
         	alert(age);// 18
         }
         
         return f2;
  }
  
   var result = f1();
   result();// 18

四、通俗易懂的闭包概念

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

由此可见,闭包就是能够读取其他函数局部变量的函数,是联系函数内部和外部的一座桥梁。

又由于在javascript中,只有函数内部的子函数才能读取函数的局部变量,所以可以把闭包理解成”定义在一个函数内部的子函数“。

五、闭包的用途

前面我们已经了解了什么是闭包,现在我们来说说闭包具体的用途。

闭包的最大用途有两个,一个是之前提到的可以读取函数内部的局部变量,另一个是可以让这些变量的值始终保持在内存中

如果不理解,可以看如下代码。

function f1(){// 此为函数f1

    var age=18;// 此为局部变量age

    ageAdd=function(){age+=1}// 此为全局变量,因为没有var关键字

    function f2(){// 此为闭包函数f2
      alert(age);
    }

    return f2;

  }

  var result = f1();

  result(); // 18

  ageAdd();

  result(); // 19

分析以上代码,不难看出result实际上就是闭包函数f2。它一共运行了两次,第一次是直接获取函数f1的局部变量age,其值为18;第二次是先执行了ageAdd,对函数f1的局部变量age做了数据更改,然后再获取值,其值为19。由此可见,在第一次运行结束后,函数f1的局部变量age并没有被自动清除,而是一直保存在内存中,第二次运行时,再对其进行操作并读取出来。

那为什么局部变量age没有被清除,反而一直存在内存中呢?原因在于f1是f2的父函数,而f2被赋值给了一个全局变量result,这就导致f2始终在内存中,而又因为f2的存在依赖于f1,所以f1也始终在内存中,并不会在调用结束后,被垃圾回收机制回收。

注意:代码中的ageAdd在声明的时候由于并没有使用关键字var,所以ageAdd是一个全局变量。ageAdd的值是一个匿名函数,而这个匿名函数本身也是一个闭包,所以ageAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

六、使用闭包时的注意事项

通过以上分析可知:

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

2、闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当做对象(Object)使用,把闭包当做它的公用方法(Public Method),把内部变量当做它的私有属性(Private Value),在使用时就不要随便改变父函数内部变量的值。

以上就是有关闭包的详细解释。

原文链接:www.ruanyifeng.com/blog/2009/0…