[js]关于闭包与访问数组下标的老生常谈

闭包的问题,想必给学习js的新手们造成了成吨的暴击,尤其是下面这个问题:


 <body>
  <p id="p0">段落0</p>
  <p id="p1">段落1</p>
  <p id="p2">段落2</p>
  <p id="p3">段落3</p>
  <p id="p4">段落4</p>

<script type="text/javascript">
 for( var i=0; i<5; i++ ) {
   document.getElementById("p"+i).οnclick=function() {
     alert(i); //访问了父函数的变量i, 闭包
   };
 };

</script>
</body>

这个代码,我是在网上找的一个典型问题,即所谓:闭包只能访问到外部函数变量的最终值。

这个结论大家都已经知道,但到底为什么,网上的说法大都比较复杂难懂。这里我找了一个比较简单的方式,来解释下。

先总结一句,即值传递和引用传递的区别。


先来看段简单的代码:

function A(){
    var res=[];
    for(var i=0;i<5;i++)
    {
        res[i]=i;
        }
    alert(res[0]);
}
A();

这个代码运行时, 会输出0,那么它正确的访问了下标;


然后我们稍作改动:

function A(){
    var res=[];
    for(var i=0;i<5;i++)
    {
        res[i]=function(){return i};
        }
    alert(res[0]);
}
A();

这个时候,一般新手会认为,输出0 或者5,其实都不对,正确答案是输出一段匿名函数的代码:function(){return i};

好,关键点来了! 为何在我们使用匿名函数后,会出来个这么东西呢? 如果有c语音基础的应该知道,那就是在传递函数时,并非值传递而是引用传递。也就是说,   res[i]=function(){return i};

这段代码,会把值为匿名函数地址的指针传递给 变量res[i],而并非把i传递给res[i]。新人一般会误把变量i和代码执行划等号,其实根本是不同的东西


那这会造成什么后果?那就是res[i]变成了匿名函数的函数名了,对res[i]进行自调用的话:(res[0])();,就会出现,我们之前所讨论的问题,那就是为什么i变成了5:为什么呢?很简单,那就是每次调用匿名函数时,都会重新运行一遍匿名函数的代码。也是说会重新根据作用域链来找i的值。而循环在输出语句之前,循环结束,i自然变成了5,此时运行匿名函数的代码,找到的自然是5;


值传递和引用传递是不一样的,值传递的话,res[0]在内存中会直接存储对应的变量i的值:0,而引用传递,则会让res[0]去存储一段代码的地址即指针,每次访问res[0],其实就等同于访问匿名函数的运行结果。而循环在访问之前就已经运算结束,i的值已经变成了5,自然就会输出5了。


在让我们来看最初的代码:

  document.getElementById("p"+i).οnclick=function() {
     alert(i);

当触发onclick时,其实不是简单输出变量i,而是去访问匿名函数的代码的执行结果。相当于是去访问现在i的值,而不是过去那个循环中的i的值。 所以才会导致总是输出i的最终值的结果。



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值