我对闭包的理解

简单来说,闭包就是在另一个作用域中保存了一份它从上一级函数或作用域取得的变量(键值对),而这些键值对是不会随上一级函数的执行完成而销毁.总而言之,是javascript代码运行时读取变量的一个机制.

闭包可以通过作用域链来解释,闭包内部通过函数的内部属性[[scope chain]]来实现.

看个例子(ig.1):

1 var obj={m1:'visited',m2:'changed'}
2 var rm={};
3 for (var m in obj){
4     rm[m] = function(){
5         alert(obj[m]);
6     }
7 }
8 rm.m1() //changed  为什么呢?
9 rm.m2() //changed  

如果觉得这个例子(ig.2)太easy的话,再对比看一个:
var obj={m1:'visited',m2:'changed'}
var rm={};
for (var m in obj){
    rm[m] = (function(m){
        return function(){
            alert(obj[m]);
        }
    })(m);//自执行是为了产生并添加一个AO到所要返回的函数的作用域链上面
}
rm.m1() //visited  为什么呢?
rm.m2() //changed  

来解释一下:

在例子ig.1中,在全局作用域内定义了两个函数rm.m1和rm.m2,也就是rm.m1和rm.m2的作用域链都是 [自身的 AO]+[全局作用域],而在调用这两个函数的时候,被访问的[全局作用域]的变量m已是固定值"changed",因此都弹出"changed".

在例子ig.2中,rm.m1和rm.m2不是定义在全局作用域中,而是在两个匿名函数中.匿名函数在定义同时执行一次,将变化的m通过值传递保存到匿名函数的实参中.此外,rm.m1和rm.m2的作用域链是 [自身的AO]+[匿名函数的AO]+[全局作用域], [匿名函数的AO]分别保存了实参"visited"和"changed",因此有以上结果.

好,闭包是什么?闭包是一个作用域中保存了一份它从上一级函数或作用域取得的变量(键值对),而这些键值对是不会随上一级函数的执行完成而销毁.

为了验证你的理解,再来看一个大大的例子:

 1 function User( properties ) {    
 2     //这里一定要声明一个变量来指向当前的instance    
 3     var objthis = this;    
 4     for ( var i in properties ) {    
 5         (function(){    
 6                 //在闭包内,t每次都是新的,而 properties[i] 的值是for里面的    
 7                 var t = properties[i];    
 8                 objthis[ "get" + i ] = function() {return t;};    
 9                 objthis[ "set" + i ] = function(val) {t = val;};    
10         })();  //自执行是为了产生一个AO添加到定义的user.getname和user.getage的作用域链上面
11     }    
12 }    
13      
14 //测试代码    
15 var user = new User({    
16     name: "Bob",    
17     age: 44    
18 });    
19      
20 alert( user.getname());    
21 alert( user.getage());    
22      
23 user.setname("Mike");    
24 alert( user.getname());    
25 alert( user.getage());     

你答对了吗?

闭包的广义理解:函数都是闭包.之所以这么说,是因为函数外面不能访问函数内部定义的变量,而函数内可以访问外部定义的变量,这称之为函数的作用域.  而当多个函数发生嵌套时,作用域也有嵌套的层级关系,称之为作用域链.作用域链在函数定义的时候就已经固定下来,因此,不论函数何时何地执行,都会找到最初定义时候的作用域链并用来查找相应的变量.

嵌套的函数能够产生作用域链,或者说改变作用域链,那还有没有其他的途径来改变作用域链?有的,除了函数的嵌套,还有with,catch也能改变作用域链.它们的共同之处时执行到with,catch代码块时会添加一个对象作为作用域链的最前端.

再看一个例子:

var obj={m1:'visited',m2:'changed'}
var rm={};
for (var m in obj){
     val=obj[m]
     with({v:val}){ //将对象{v:val}添加到代码块里面定义的函数作用域链上面
         rm[m] = function(){
             alert(v);
         }
     }
 }
 rm.m1() //visited  为什么呢?
 rm.m2() //changed
var obj={m1:'visited',m2:'changed'}
var rm={};
for (var m in obj){
    try{
        throw  v=obj[m];
    }catch(v){ //将包含属性v的对象添加到代码块里面定义的函数作用域链上面
        rm[m] = function(){
             alert(v);
         }
    }
 }
 rm.m1() //visited  为什么呢?
 rm.m2() //changed

 

更多阅读:

http://www.cnblogs.com/rubylouvre/archive/2009/07/24/1530074.html

posted @ 2013-03-21 23:27  果皮  阅读(146)  评论(0)    收藏  举报