JavaScript 函数之 ------------------ 闭包

Posted czhyuwj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript 函数之 ------------------ 闭包相关的知识,希望对你有一定的参考价值。

      谈到闭包,人们常常会把匿名函数和闭包混淆在一起。闭包是指由权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数,仍以前面的

createComparisonFunction()函数为例

  

 1           function createComparisonFunction(propertyName){
 2                     return function(object1,object2){
 3                         var value1 = object1[propertyName];
 4                         var value2 = object2[propertyName];   
 5                         if(value1<value2){
 6                             return -1;
 7                         }else if(value1>value2){
 8                             return 1;
 9                         }else{
10                             return 0;
11                         }
12                     }
13                 };

  在标识的部分,它访问了外部的变量 propertyName 即使这个函数被返回后,仍然可以访问到这个变量(propertyName);这是应为 匿名函数作用域链中包含  createComparisonFunction() 的作用域。

    要理解必须掌握作用域的几个概念

  作用域链:

    什么时候会生成?:当代码在一个执行环境中执行时,就会创建变量对象的一个作用域链。

    作用:保证对执行环境有权访问的所以函数和变量的有序访问。作用域的前端永远是当前的执行环境所对应的变量对象;

  执行环境:定义了变量或函数有权访问其他数据,决定它们各自的行为。 每个环境都有一个与之对应的变量对象(variable object)。环境中定义的变量和函数都保存在这个对象中。 当函数执行完成后其执行环境就会被销毁

  变量对象:保存执行环境中的变量和函数。

  活动对象:如果执行环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含一个变量。

   

   在函数执行过程中,为读取和写入变量的值,就需要的作用域链中查找对应的变量。先看看一个实例来了解下每个概念对应部分: 

   

 1  function compare(value1,value2){
 2  
 3     if(value1<value2){
 4         return -1;
 5     }else if(value1>value2){
 6         return 1;
 7     }else{
 8         return 0;
 9     }
10   }   
   var result = compare(5,10);
  

   

  先定义了 compare 函数,后又在全局作用域中调用,在这个compare 执行环境为函数的时候,当前活动对象则为变量对象。 活动对象默认是只有 arguments 但是此时却不一样(this,arguments,value1,value2);

而全局作用域中对应变量对象(compare 、result);来看看下图吧!

 

  <1>在定义compare函数的时候,会创建一个预先包含全局变量对象作用域链,这个作用域链保存在内部的[[Scope]]属性中。

  <2>当在调用 compare 函数的时候,会为函数创建一个执行环境,然后通过赋值函数的[[Scope]]属性中的对象构建起执行环境的作用域链。此后,又有一个活动对象被创建并推入执行环节作用域链的前端。

对于compare函数的执行环境而言,其作用域链中包含两个变量对象(本地活动对象和全局变量对象)。显然,作用域链本质上是一个指向变量对象的指针列表,它引用但不实际包含变量对象。

所以无论什么时候,访问一个变量时都会从作用域链中搜索具有相应名字的变量。一般来讲当函数执行完毕后。局部活动对象就会被销毁,内存中仅保存全局作用域。但是闭包所有不同。

 

  在闭包中,当函数内部定义一个内部函数,该内部函数会把外部函数的活动对象添加到自己的作用域链中;因此createComparisonFunction()定义的匿名函数,其作用于域会包含外部函数的作用域。

 1           function  createComparisonFunction(propertyName){
 2                     return function(object1,object2){
 3                         var value1 = object1[propertyName];
 4                         var value2 = object2[propertyName];   
 5                         if(value1<value2){
 6                             return -1;
 7                         }else if(value1>value2){
 8                             return 1;
 9                         }else{
10                             return 0;
11                         }
12                     }
13                 };    

当下面代码执行时,外部函数与内部函数包含的作用域链, 如下图

1 var compare = createComparisonFunction("name");
2 var result = compare({name:"Nicholas"},{name:"Greg"});

 

在代码执行过程中,用变量 compare 保存了其返回的内部匿名函数,而作用域链中包含了三个变量对象,(全局变量对象,外部函数-活动对象,匿名函数-活动对象);

所以即使返回后,也还是可以访问到对应的变量; 因为外部函数(createComparisonFunction) 执行完成时,其活动对象依然存在,并不会销毁,因为其被内部函数作用域链中引用了;

虽然 外部函数返回到,其执行环境对应作用域链被销毁,但是其活动对象依然存在。直到匿名函数被销毁,其createComparisonFunction对应的活动对象才会被销毁。

1 var compare = createComparisonFunction("name");
2 var result = compare({name:"Nicholas"},{name:"Greg"});
3 compare = null;  //主要是手动赋值为空,通知系统 进行垃圾处理; 随着其作用域链被销毁(除全局作用域链之外)。

由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内部占用过多。

    

以上是关于JavaScript 函数之 ------------------ 闭包的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript学习笔记——JavaScript语法之函数

JavaScript之函数

JavaScript系列之回调函数callback

JavaScript之闭包

JavaScript之深入理解函数

JavaScript之函数