关于javascript的闭包

  闭包的定义:

  闭包是函数式编程中的概念(lisp属于典型的函数式编程语言),其严格定义为:函数(环境)和其封闭的自由变量组成的集合体。通常我们在一个函数内定义了一个新函数,这个新函数内部引用了父函数中定义的变量,然后新函数被返回,这样就形成了一个闭包(当一个函数返回它内部定义的一个函数时,就形成了闭包,闭包不但包括被返回的函数,还包括这个函数的定义环境)。示例代码如下:

 1 function outer() {
 2     var count = 1;
 3     var get = function() {
 4         count ++;
 5         return count;
 6     }
 7     return get;    
 8 }
 9 
10 var new = outer();
11 new();    //2
12 new();    //3
13 new();    //4
14 ...

  按照命令式语言思维理解,当outer函数运行完之后,其内部申请存储count变量的空间将会被释放,返回的函数将不可能引用到count变量。然而,由于函数式编程中的闭包特性,当outer函数在调用栈中退出时,由于返回了outer函数中内部定义的函数实例,而这个被返回的函数实例引用了count变量,那么count变量就会在内存中存有一份副本。

  注:函数返回内部定义的函数,返回的是一个实例。具体示例如下:

 1 var generate = function() {
 2     var count = 0;
 3     function get() {
 4         count ++;
 5         return count;      
 6    }
 7    return get;
 8 }
 9 
10 var counter = generate();
11 var counter1 = generate();
12 
13 counter()     //1
14 counter1()   //1
15 counter()    //2
16 counter()    //3
17 counter1()  //2

  详细解释上面的示例代码:两次调用generate函数将会分别返回独立的get函数数实例,并且get函数实例函数的运行环境(作用域)也分别被返回了(分别在内存中存有一份副本)。这样分别counter和counter1就是两个独立的函数,并且它们的运行环境也是完全独立的。counter和counter1的调用互不干扰。

  闭包的用途:

  1.嵌套的回调函数:

  如下的ajax回调函数:

 1 exports.add_user = function(user_info, callback) {
 2     var uid = parseInt(user_info['uid']);
 3     mongodb.open(function(err, db) {
 4         if (err) {callback(err); return;}
 5         db.collection('users', function(err, collection) {
 6             if (err) {callback(err); return;}
 7             collection.ensureIndex("uid", function(err) {
 8                 if (err) {callback(err); return;}
 9                 collection.ensureIndex("username", function(err) {
10                      if (err) {callback(err); return;}
11                      collection.findOne({uid: uid}, function(err) {
12                          if (err) {callback(err); return;}
13                          if (doc) {
14                              callback('occupied');
15                           } else {
16                           var user = {
17                                              uid: uid,
18                                              user: user_info,
19                                             };
20                            collection.insert(user, function(err) {
21                                 callback(err);
22                             });
23                         }
24                     });
25                });
26             });
27         });
28     });
29 };                                                            

  解释上面的代码:以上示例是用回调嵌套实现的将用户信息存入mongo的函数,其实质是一个闭包的嵌套。每个回调是在相应的请求完成后由请求函数进行回调,请求函数虽已执行完毕,但由于里层的函数有可能引用请求函数作用域中的变量,所以每个请求函数中变量都不会被释放。就像示例代码中所看到的,最里层的回调函数还引用了最外面的uid变量。

  实现对象中的私有属性:

  在javascript中对象没有私有属性,换言之对象的每一个属性都是暴露给外部的。这样会引发一系列的安全问题,例如兑现的使用者直接修改了对象的某个属性,导致对象内部数据一致性遭到破坏。Javascrip可以通过在属性前加上一个下划线(_privateProp)约定表示某个属性是私有属性,外部对象不应该直接使用它。但这只是个约定,你们懂的,世界上总有那么些不按约定办事的人,这样的约定并不能阻止外部对象直接访问某个私有属性。通过闭包,可以实现真正的私有属性。

  回到前面的代码:

 1 function generate() {
 2     var count = 0;
 3     var get = function() {
 4         count ++;
 5         return count;
 6     }
 7     return get;      
 8 }
 9 
10 
11 var counter = generate();
12 
13 counter()    //1
14 counter()    //2
15 counter()    //3
16 ...

  在javascript中函数也是对象,因而函数中定义的变量,也算是函数对象的属性,虽然我们无法通过函数对象直接访问到这个属性,但是我们可以通过形成get的闭包函数,这样我们可以通过闭包函数去获取这个属性。

 

posted @ 2017-01-14 15:08  snicker  阅读(77)  评论(0编辑  收藏  举报