函数表达式和闭包

  简述:

  最近学习了javascript函数表达式和闭包这一块, 记录下自己的学习笔记! 

  学习ECMAscript,函数表达式和'面向对象'这二块是难点,也是重点。

  参考书:<javascript高级程序设计> 工具: EditPlus 浏览器 IE11 ,chrome

  

  1  javascript定义函数的方法主要有二种:

  (1)函数声明

   function p(){//code} 

 (2)函数表达式: 也叫匿名函数

   var p=function(){};

  简单阐述有什么区别?

  

1 <script type="text/javascript">
2   <!--
3      p();
4      function p(){
5          alert('函数声明');
6     }
7   //-->
8   </script>

   使用函数声明的方式,上面这样调用没有问题,原因是javascript存在'函数声明提升'。但是var p=function(){} 这样定义就会出错,原因是找不到这个函数(不存在函数声明提升);

 

2 函数的一些特征:

   (1)Function对象实例  

      函数名是指针,函数是对象;所以函数是没有重载的;

<script type="text/javascript">
   function a(){alert('a');}
   function a(){alert('123');}
   a(); 
</script>

 结果:123    很简单:第二个函数和第一函数是不同的对象,a现在指向它,不再指向第一个函数对象,访问的自然也就是第一个函数。

   (2)既然是实例:那么就会有方法和属性---要有这个意识

    比如caller 可以获取调用这个函数的函数的引用;  可以看看下面代码

    

1         function a(){
2           b();
3      }
4      function b(){
5         alert(b.caller);
6      }
7      a(); // function a(){b();}

    (3)二个重要的内部属性 arguments,this 

   this不用多说,谁调用这个函数就代表谁,注意:函数是一定有调用者的,不可能自行调用。

   arguments 是用来存放函数参数的一个对象,这个对象有个叫callee的属性,代表拥有该arguments的函数,换句话说是正在运行的函数引用

   可以看看下面代码

   

   function c(){
        alert(arguments.callee);
   }
   c();//function c(){//code}

         

   3 函数中定义变量

     定义变量可以这样 var x=1; x=0;区别就在有没有var声明,在window全局中基本没有区别,但是function中是不一样的;一句话来说,var声明

    就是局部变量,不声明那就是window的变量(全局变量);看看下面代码

   

1 function d(){
2       name='window name';
3     }
4     d();
5     alert(window.name);//window name

   第2行改为var name='window name' 第5行报错,undefined  

    函数表达式基础的差不多了,接下来重点来了!

  4 作用域链

  理解作用域链是理解闭包的关键;

  (1)什么是执行环境

   执行环境有二种: 全局执行环境;局部执行环境---function里面;

   执行流进入函数执行时,创建执行环境;函数执行结束,回收! 

  (2)变量对象

   理解变量对象非常重要,变量对象在函数中也称为 活动对象,我还是喜欢说成变量对象。

   每一个执行环境都有一个变量对象,变量对象会保存这个执行环境的变量和方法;我们不难想到全局的变量对象是谁? window对象

  我们在全局中添加变量和方法都是添加到window里。函数执行时 执行环境中就会创建变量对象;一般来说:函数调用完毕之后无论是执行环境(出栈),还是变量对象都是回收的。闭包的出现使得变量对象不回收;

  (3)作用域链?

  什么是作用域链:作用域链的本质是 指向变量对象的一组有序指针;字面上很难理解,我第一次看不明白!

  有什么作用:在一个执行环境中对变量和方法进行有序(正确)访问;  

 什么时候创建: 一个函数声明的时候就创建了,作用域链会保存在一个函数的内部属性[[Scope]]中;

  注意:执行流进入函数是: 创建执行环境->将作用域链(利用[[Scope]]属性) 复制到执行环境中->创建变量对象(活动对象)->将变量对象的引用(指针)导入作用域链的最前端->执行代码

  具体请看下面的代码

  

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

  作用域链图解

  

   我们可以看到,作用域链是什么:有序指针,前面说的作用域最前端在图中就是0位置。 查找变量或者函数是最前端开始的,0指向的活动对象没有你要找的变量,再去1找。0-1其实

   从代码的角度上看其实就是从函数内部一直向函数外部找标识符(变量或者函数),找到立即停止,不会再向上查找。这就是作用域链!

   

  5 闭包

   定义: 闭包是有权访问另外一个函数作用域变量的函数;注意闭包是一个函数!

   创建闭包的主要的方法:在一个函数里面创建一个函数(闭包); 比如

  

<script type="text/javascript">
    function A(value){
     var num=value;
    return function(){ //闭包
     alert(num);
  }
}
var B = A(0);
alert(B()); //0
</script>

   在这里匿名函数是一个闭包,一般情况下: 一个函数执行完毕之后,无论是作用域链还是变量对象都会回收,但是这个匿名函数的作用域链里有A()变量对象的引用,所以没有消除。

   还可以访问变量对象的num;这就是闭包的好处!但是闭包的出现加大内存负担,就这里而已,我们即使后面不再使用B函数了,A()变量对象也不会消失,javascript不知道你什么时候还会再用,当然我们可以这样 B=null; 这样的话匿名函数没有引用,被回收,A()变量对象也一起回收!

 《javascript高级程序设计》中尼古拉斯大神建议我们:可以不使用闭包尽量不使用,万不得已才使用!  

   

 6  块级作用域 

  我们都知道javascript是没有块级作用域的,比如{}; if(i=0){}   while(){}  for(){}这些都不会形成块级作用域; 那么怎么创建 java c#类似功能的块级作用域?

  语法:

 (function(){

   //块级作用域

})();

  注意:  我们创建一个匿名函数,立即调用,里面的代码都要运行一遍,而在window中看不见,这不就是块级作用域吗? 还有一个好处,这个匿名函数没有指针,

 调用后回收,不会产生垃圾(里面的方法,变量都不需要再访问的)。简直就是完美的块级作用域! 注意格式 (匿名函数)其实就是一个指针,再加上()就是调用了。

 

 7 构造函数中的闭包

   (1) 我们知道怎么为对象添加'私有变量' 这样

    

1    function Person(name,age){
2     this.name=name;//共有属性
3     this.age=age;
4     
5     var school="一中"; //私有属性
6     this.GetSchool=function (){return school;}
7 }

      我们这个school是私有的变量,因为闭包的原因, 对象实例自然可以访问这个变量; 比如  var p=new Person('nos',20); p.GetSchool(); // 一中; 

     (2)现在来一个奇葩: 静态私有属性 ;

      

  (function(){

         var school=""; //静态私有;

        Person=function(name,age,value){ //构造函数

         this.name=name;this.age=age;

          school=value;

     }; 

    Person.prototype.GetSchool=function(){alert(school);}

         })();
      var p=new Person('andy',21,'一中');
      p.GetSchool();//一中
      var pp=new Person('nos',29,'二中');
      pp.GetSchool();//二中
      p.GetSchool();//二中

        从结果上看 school是对象共有的,私有的属性, 即静态私有属性; 

       我们看看构造函数是怎么定义的: 没有使用var ,前面说过即使在函数里面定义,没有使用var申明,就是window的,为全局的。自然可以在全局使用! 

      这个匿名函数中调用了一次,只产生一个对象变量,school都是同一个,实现共享。

  

  

原文地址:https://www.cnblogs.com/huang-1995/p/5765878.html