匿名函数和闭包
一、匿名函数
匿名函数就是没有名字的函数,又称作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的一种高级设计模式,有时间更新。
由于闭包会携带包含它的函数作用域,因此会比其他的函数占用更多的内存,不宜过多的使用,但是用的好的话还是非常值得推荐的。