建鑫

导航

匿名函数和闭包

 

一、匿名函数

匿名函数就是没有名字的函数,又称作Lambda函数。好多资料说它强大,它到底强大在哪

1 function foo(arg1,arg2,arg3){
2     //code  
3 }
4 
5 var foo = function(arg1,arg2,arg3){
6     //code  
7 }

这两者区别在哪?

  第一个我们称作是函数声明 ,声明方式无非就是数据类型名,后面接空格和一个变量,就跟C++中的 int a ,char *c一样

  第二个函数称作函数表达式,表达式就是类似于 var a = b + c,string = stringA + stringB等

  它们的区别就是函数声明会在代码执行以前记载到作用域中,后者是在代码执行到那一行的时候才会有定义,才会去查找 a ,string到底是什么东东。

  另一个重要的区别是,函数声明给函数定义了一个名字foo ,而函数表达式是创建了一个匿名函数,这个函数谁都无法调用,但是它将指针赋值给foo以后,foo就可以去调用这个函数。

  为什么要使用匿名函数,单单的函数声明难道不够吗?

1 function foo(arg1,arg2){
2     return function(arg3,arg4){
3     //code
4     }
5 }

函数执行结果返回的函数可以赋值给一个变量,或者以其他方式调用。在把函数当成值使用的情况下都可以使用匿名函数。

note:arguments.callee指向一个正在执行的函数指针,arguments.callee.caller是执行当前函数的一个对象或者说一个环境。

二、作用域链

  在讲闭包前,先说一下作用域链,

  执行环境:execution context

  每个函数在被调用时会创建自己的执行环境,当执行流进入这个函数时,函数的环境变会被推入到一个环境栈中。当函数执行之后,栈将其环境弹出,控制权返回给之前的执行环境(其实这个执行环境就是作用域链的具体化)。

  变量对象构成一个作用域链,作用域链保证了程序的按序正常运行

三、闭包

闭包是什么,闭包就是可以访问另一个函数作用域中变量的函数。

一般来讲,当函数执行完毕之后,,局部的活动对象被销毁,,内存中只包含全局作用域。

创建方式:

  就是在一个函数中创建另一个函数。

  在匿名函数从函数foo中返回后,它的作用域链被初始化为包含foo函数的活动对象和全局变量对象。这样匿名函数就可以访问在foo中定义的所有变量。跟重要的是foo函数执行完毕之后,他的活动对象时不会被销毁的,因为匿名函数的作用域链仍然引用这个活动对象。只有当匿名函数销毁后,foo的活动对象才会被销毁。

使用范围:

  1.模仿块级作用域!

  何为块级作用域?就是在这个语句块中有定义,脱离了以后自动销毁!

  

1 function foo(num){
2     for(var i =0;i<count;i++){
3         //console.log(i);  
4     }  
5      //var i;重新声明也没用
6     
7      alert(i); 
8 }    

 

  如上,在C,C++,java又块级作用域的语言中,if ,for语句中的变量声明会在语句执行完后销毁,但是javascript会自动添加到当前的执行环境中。因此i为num,即使重新声明也会是num。因为它会对后续的声明视为不见。

  匿名函数可以用来模仿块级作用域来避免由于各种写法,本来不想定义的变量存在全局中,无法销毁的问题

1 (function(){
2     //块级作用域
3 })();

  这种技术长在全局作用域中被用在函数外部,从而限制向全局作用域中添加过多的变量和函数。一般来说我们应该尽量减少向全局作用域中添加过多的变量和函数。在一个有很多开发人员共同参与的大型应用程序中,过多的全局变量和函数会导致命名冲突,而通过创建私有作用域,每个开发人员可以使用自己的变量, 不用担心搞坏全局作用域。

  2.静态私有变量的方法:

 1 (function(){
 2     var name = '';
 3     Person = function(value){
 4         name = value;
 5     }
 6     Person.prototype.getName = function(){
 7         console.log("name",name)
 8         return name;
 9     }
10     Person.prototype.setName = function(value){
11         name = value;
12     }
13 })();
14 //静态变量只要有一个对象跟改,其他的都跟着更改
15 var p1 = new Person("我是第一");
16 var p2 = new Person('我是第二');
17 p1.getName();   //我是第二
18 p2.getName();   //我是第二
19 
20 p1.setName("我变了");
21 p1.getName();   //我变了
22 p2.getName();   //我变了

  3.模块模式初探:

前面的模式是用于自定义类型创建私有变量和特权方法。而道格拉斯所说的模块模式(Module Pattern)则是为单例创建私有变量和特权方法。所谓的单例就是一个实例的对象。

  单例:

1 var singleton = {
2     name:value,
3     method: function(){
4 
5     }           
6 }

模块通过为单例添加私有变量和方法使其增强。

在Web App中,经常需要使用一个单例来管理应用程序级的信息,比如下面的模块管理器。

 1 //模块管理器
 2 var moduleManger = function(){
 3     var _count = 0;
 4     var _module = {};
 5     
 6     var get = function(key){
 7         return _module[key];
 8     }
 9     var set = function(key,value){
10         _module[key] = value;
11         _count ++;
12     }
13     //delete
14     var remove = function(key){
15         delete _module[key];  
16         _count--;
17     }
18     return {
19         get : get,
20         set : set,
21         add : add,
22         remove : remove
23     }
24 }();

 

 

简言之,如果你必须创建一个对象并以某些数据对其初始化,同时还要公布一些能够访问这些私有数据的方法, 就可以使用模块模式。以这种模式创建的每个实例都是Object实例,最终通过一个字面量来表示它。

 

注:module pattern是javascript的一种高级设计模式,有时间更新。

由于闭包会携带包含它的函数作用域,因此会比其他的函数占用更多的内存,不宜过多的使用,但是用的好的话还是非常值得推荐的。

  

 

 

 

posted on 2013-01-12 13:05  建鑫  阅读(2118)  评论(4编辑  收藏  举报