在JavaScript中,闭包恐怕是很多人不能理解的一个概念了,甚至很多人也会把闭包和匿名函数混淆。

一、 匿名函数

 闭包是有权访问另一个函数作用域中的变量的函数。首先要明白的就是,闭包是函数。由于要求它可以访问另一个函数的作用于中的变量,所以我们往往是在一个函数的内部创建另一个函数,而“另一个函数”就是闭包。

 

 比如之前提到过的作为比较函数:

//普通函数定义:

  function createComparisonFunction(propertyName){
   return function(object1,object2){
    var value1=object1[propertyName];
    var value2=object2[propertyName];
    if(value1<value2){ return="" -1;="" }else="" if(value1="">value2){
     return 1;
    }else{
     return 0;
    }
   };
  }
</value2){>

图片 1

 在这个函数中,由于return的函数它访问了包含函数(外部函数)的变量propertyName,所以我们认为这个函数即为闭包。即使这个闭包被返回了,而且是在其他地方调用了,但是它仍然可以访问propertyName,之所以还能够访问到propertyName这个变量,是因为内部函数(闭包)的作用域链中包含着createComparisonFunction函数的作用域。因此,要彻底搞清楚闭包,就需要彻底搞清楚函数被调用时发生了什么以及作用域链的有关知识。

//单独的匿名函数是无法运行的。就算运行了,也无法调用,因为没有名称。

 当某个函数被调用时,会创建一个执行环境(函数一旦被调用,则进入函数执行环境)和相应的作用域链(作用域链是随着执行环境的不同而动态变化的)。(对于函数而言)之后使用arguments和其他命名参数的值来初始化函数的活动对象(每个执行环境都有一个变量对象,对于函数成为活动对象)。对于有闭包的函数而言,在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象始终处于第三位。。。直至作为作用域链终点的全局执行环境。

如: function(){

 下面撇开闭包不谈,先通过一个简单的例子来理解作用域链以及变量对象和活动对象。

            alert(‘123’);

 function compare(value1,value2){
 if(value1<value2){
  return -1;
 }else if(value1>value2){
  return 1;
 }else{
  return 0;
 }
}<br>         var result=compare(5,10);

        }                        //语法错误,无法执行

   
以上代码首先定义了compare()函数,然后又在全局作用域中调用了它。当调用compare函数时,首先创建一个函数执行环境,每个执行环境又对应这一个变量对象,也就是说作用域链和函数执行环境是同时创建的,其中作用域链的前端即为compare函数的活动对象(在函数中,变量对象又称为活动对象)。在compare活动对象中包含了arguments、value1、value2(关键:尽管arguments数组对象包含value1和value2,但是我们还是要分开列举,而不是仅仅认为只有arguments包含于compare的活动对象,因为value1和value2也包含于compare的活动对象)。

 

 对于上述代码而言,全局执行环境的变量对象(再次声明:每一个执行环境都存在相应的变量对象)中包含result和compare,该变量对象在compare()执行环境的作用域链的第二位。

1.简单的使用:

 当我们创建compare()函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在compare函数内部的[[scope]]属性中,当调用compare函数时,会为函数创建一个执行环境,然后通过复制函数的[[scope]]属性中的对象构建起执行环境的作用域链。如下:

    var box =  function(){

图片 2

        return ‘Lee’;

  作用域链的本质就是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。无论什么时候在函数中访问一个变量,就会从作用域链的前端沿着作用域链搜索具有相应名字的变量。我们知道,全局环境的变量对象始终存在,而局部环境(如compare()函数执行环境)的变量对象只在函数执行的时候存在,一旦执行完毕,局部变量对象(活动对象)就会被销毁。但在闭包中,却与此不同。

    }

  把博文开始的代码复制如下:

    alert (box());        //需要调用box()方法。

 function createComparisonFunction(propertyName){
 return function(object1,object2){
  var value1=object1[propertyName];
  var value2=object2[propertyName];
  if(value1<value2){
   return -1;
  }else if(value1>value2){
   return 1;
  }else{
   return 0;
  }
 };
}

 

由于在一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。因此,在createComparisonFunction函数内部定义的匿名函数的作用域中实际包含着外部函数的活动对象。如果我们执行如下代码:

2.通过自我执行来执行匿名函数

var compare=createComparisonFunction("name");
var result=compare({name:"zzw"},{name:"ht"});

    (function(){                //语法格式:(匿名函数)(); 
 第一个圆括号放匿名函数,第二个圆括号执行

这时候匿名函数的作用域链将引用着外部函数的活动对象。因为匿名函数从外部函数中被返回后,它的作用域链被初始化为包含外部函数的活动对象和全局变量对象。这样,匿名函数就可以访问外部函数中定义的所有变量。更为重要的是,即使外部函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。换句话说,当createComparison()函数返回后,其执行环境的作用域链会被销毁,但是她的活动对象仍然保存在内存中。等到倪敏函数被销毁后,外部函数的活动对象才会被销毁。

        alert(‘123′)

由于闭包会携带者包含他的函数的作用域,因此回避其他函数占用更多的内存。过度的使用闭包可能会导致内存占用过多,我们建议只在绝对必要的时候再考虑使用闭包。

    })();

模仿块级作用域

 

(function(){
 var now=new Date();
 if(now.getMonth()==0&&now.getDate()==1){
  alert("happy new year");
 }
})();

//把匿名函数自我执行的返回值赋给变量

这就是模仿块级作用域,即定义并立即调用了一个匿名函数。

    var box =  (function(){       

如下为演示其作用:

          return 200;

function outputNumbers(count){
 (function(){
  for (var i=0;i<count;i++){
   console.log(i);
  }
 })();
 console.log(i);
}
outputNumbers(5);

       })();

这是在模仿块级作用域之外的console.log(i)就会导致错误,因为i未被定义。说明在执行了模仿块级作用域之后,内部的变量就被销毁了。

    alert(box);                //参考以上1,直接调用box变量。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,同时也希望多多支持脚本之家!

 

您可能感兴趣的文章:

  • JavaScript
    String.replace函数参数实例说明
  • javascript,php获取函数参数对象的代码
  • javascript
    函数参数原来是可以有缺省值的
  • javascript
    setTimeout()传递函数参数(包括传递对象参数)
  • Javascript
    闭包详解及实例代码
  • 学习Javascript闭包(Closure)知识
  • js实现为a标签添加事件的方法(使用闭包循环)
  • JavaScript中闭包的写法和作用详解
  • JavaScript闭包实例详解
  • 一分钟理解js闭包
  • javascript从作用域链谈闭包
  • 深入理解javascript函数参数与闭包

3.通过自我执行匿名函数传参

    (function(age){

        alert(age);

    })();

 

4.函数里面放一个匿名函数

    function box () {

         return function () {

             return ‘pjh’;

         }

     }

     alert(box()());

     //另外一种调用方法(更加方便):

     var b = box();

     alert(b());

 


 

二、闭包

   
闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常用方式,就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量。

 

   
使用闭包有一个优点,也是它的缺点;就是把局部变量驻留在内存中,可以避免使用全局变量。(全局变量污染导致应用程序不可预测性,每个模块都可以调用必定引来灾难!所以推荐使用私有的,封装的局部变量)。

 

  1. 使用匿名函数实现局部变量驻留内存从而累加。

        function box () {

            var age = 100;

            return function(){

               age++;

               return age;

            }

        }

        var b = box();

        alert(b());

        alert(b());

        alert(b());

        b = null;      //解除引用,等待垃圾回收

        alert(b);        //程序将报错,因为b已经销毁不存在了!

 

PS:由于闭包里作用域返回的局部变量资源不会被立即销毁回收,所以可能会占用更多的内存。过度使用闭包会导致性能下降,建议在非常有必要的情况下才使用闭包。

 

  1. 通过闭包的自我执行来实现数组的遍历打印

        function box () {

            var arr = [];

            for (var i=0;i<5;i++) {

                arr[i] = (function (num) {

                     return function () {        

                          return num;

                     }

                  })(i);

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图