大家帮我理解下关于js中闭包的两个概念

hexinping123 2012-01-07 10:58:00
第一个问题:

在网上看了一些闭包的帖子,书上也讲过一些,有一句很经典的话是说“没有内存就没有闭包”,我的理解是,闭包的作用域链中会包含外部函数活动对象的作用域链,当外部函数在全局环境中定义时,外部函数就变成了全局函数,就会占用内存,而闭包的作用域链中含有外部函数的作用域链表,所以当闭包函数执行完毕后,闭包函数中的变量也不会消失,因为闭包的作用域链没有消失……占用了外部函数的作用域链,所以就占用的内存

请问,我这样理解是否正确???


———————————————————————————————————————————————————————
第二个问题:
在书上看到这么一段话,晒出来给大家看看,我没法理解

“闭包与变量”

作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。闭包所保存的是整个变量对象,而不是某个特殊的变量。下面这个例子可以清晰的说明这个问题

function createFunction(){
var result=new Array();
for(var i=0;i<10;i++)
{
result[i]=function(){
return i;
}
}

return result;
}

var funcs=createFunction();

//每个函数都输出10
for(var i=0;i<funcs.length;i++)
{
document.write(funcs[i]()+"<br/>")
}

这个函数会返回一个函数数组,表面上看,似乎每个函数都应该返回自己的索引值,即位置0的函数返回0,位置为1的位置函数返回1,以此内推。但实际上,每个函数都会返回10.因为每个函数的作用域链中保存这个createFunction()函数的活动对象,所以它们引用的都是同一变量i。当createFunction()函数返回后,变量i的值都是10,此时每个函数都引用着保存变量i的同一个变量对象,所以每个函数内部i的值都是10,但是,我们可以通过创建另一个匿名函数强制让闭包的行为符合预期,如下所示:

function createFunction(){
var result=new Array();
for(var i=0;i<10;i++)
{
result[i]=function(num){
return function(){
return num;
};
}(i);
}
return result;
}

var funcs=createFunction();

//分别输出0、1、2、……9
for(var i=0;i<funcs.length;i++)
{
document.write(funcs[i]()+"<br/>")
}

在重写了前面的createFunction()函数后,每个函数就会返回各自不同的索引值,在这个版本中,我们没有直接把闭包赋给数组,而是定义了一个匿名函数,并立即执行了该匿名函数个结果赋给数组,这里的匿名函数有一个参素num,也就是最终要返回的值,在调用这个匿名函数时,我们传入了变量i,由于函数参素是按值传递的,所以会将变量i的当前值复制给参素num。而在这个匿名函数内部,又创建并返回一个访问num的闭包,这样一来,result数组中的每个函数都有自己的num变量的一个副本,因此就可以访问各自不同的数值了。


上面书上的一些解释,我弄不懂,希望高手可以帮我简单解释下……
...全文
91 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
hexinping123 2012-01-09
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 sharkdbj 的回复:]
JScript code

function createFunction(){//createFunction函数申明
var result=new Array();
for(var i=0;i<10;i++)
{
result[i]=function(){//result数组存放申明的函数,注意这里函数只是申明但是并没有执行
return i;
}
……
[/Quote]

非常感谢,应该弄明白了,分全给你吧 呵呵
咸鱼boris 2012-01-09
  • 打赏
  • 举报
回复

function createFunction(){//createFunction函数申明
var result=new Array();
for(var i=0;i<10;i++)
{
result[i]=function(){//result数组存放申明的函数,注意这里函数只是申明但是并没有执行
return i;
}
}

return result;
}

var funcs=createFunction();//执行createFunction函数

//每个函数都输出10
for(var i=0;i<funcs.length;i++)
{
document.write(funcs[i]()+"<br/>") //执行result数组中的函数
}

从上面可以看到,createFunction函数先执行,而result数组中的函数后执行,我们来看下变量的改变
createFunction执行完,i = 10,执行result数组中的函数,函数用到i变量,它便去它的作用域中找是否有申明过的有效的i变量,即function(){ return i; }函数执行作用域,发现找不到,那么它就去它的父级作用域找,即createFunction执行作用域,发现此时i已经变为10,那么result数组中执行函数返回也就是10了。
如果让result数组在申明函数时立即执行该函数,那么也就不会出现类似的问题:

function createFunction(){//createFunction函数申明
var result=new Array();
for(var i=0;i<10;i++)
{
result[i]=(function(){//立即执行函数,需要有匿名函数的概念
return i;
})();
}

return result;
}

var funcs=createFunction();//执行createFunction函数

//每个函数都输出10
for(var i=0;i<funcs.length;i++)
{
document.write(funcs[i]+"<br/>")
}

此时result数组存放的是立即执行函数执行完返回的值,此时执行顺序为在执行createFunction函数时就已经依次执行了result数组处的立即执行函数
显然我们不希望在createFunction执行时在result数组中存放的是函数执行完的返回值,我们想在后续的操作中再去执行result数组中的函数,这就是上面介绍的方法的应用:


function createFunction(){
var result=new Array();
for(var i=0;i<10;i++)
{
result[i]=function(num){//立即执行函数创建一个执行作用域,并返回一个函数
return function(){
return num;
};
}(i);
} return result;
}

var funcs=createFunction();

//分别输出0、1、2、……9
for(var i=0;i<funcs.length;i++)
{
document.write(funcs[i]()+"<br/>")
}

如上述注释中我们可以看到,result数组通过立即执行函数返回一个函数 function(){return num;},也就是说当createFunction执行时result数组被赋予这个函数,在后续的result数组中的函数被执行时,函数也会像第一个介绍的那样去作用域中找变量,这里要找的是num,显然自己的作用域函数找不到num的申明和赋值,那就去父级作用域找,注意此时父级作用域已经改变,不是createFunction,而是匿名函数:
function(num){
//code
}(i);
匿名函数的num变量已经被赋予i值,匿名函数在createFunction函数执行时已经执行,也就是说num值并不是都是最后的10,而是1~10的值,此时result数组执行的函数中返回的值也是1~10

这样讲还算清楚吗?楼主不明白匿名函数是什么东西先去学学匿名函数吧

87,917

社区成员

发帖
与我相关
我的任务
社区描述
Web 开发 JavaScript
社区管理员
  • JavaScript
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧