献给各位新手——真正搞清楚javascript闭包(1)

闭包这个东西,对js新手们来说确实不好理解,我自己在学习的时候,不夸张,看了不下20篇网上讲闭包的帖子,js的参考书也看了一堆,这里看懂点,哪里看懂点,最后综合起来,总算是搞清楚了。
    在我自学的过程中,我觉得网上的帖子要么太晦涩,要么没讲清楚,总之就是没有一篇特别适合新手们学习的帖子,我今天就结合各种实例仔细讲解(我把w3cfun 中之前 讲解闭包的精华帖中的例子都拿出来讲解,因为我觉得之前的帖子没有把实例讲解清楚,我当初看时也不懂为啥会是这样的运行结果),也当是自己复习梳理。 
     我相信,看完我的帖子,你再去看  为之漫笔  先生翻译的《理解javascript闭包》,就不会那么吃力了(我刚开始最少看了10遍也不懂)
                 献给新手,老鸟们请飘过!!  文章中可能会有用词不严谨的地方,但是保证新手们一定能看懂。

    ok,废话不多说,进入正题。
    就想大家在n多讲解闭包帖子中看到的,要搞懂这个东西,作用域、执行环境(也叫运行上下文),作用域链的概念是必须先懂的,这个是前提,这个不懂,后面的就是在扯淡~~

    第一个实例  (这也是之前精华帖中的实例,请大家仔细看我下面的分析,会很长,但是会说明白原理)

   function outerFun()
{
var a =0;
alert(a); 
}
var a=4;
outerFun();     //0
alert(a);          //4



      明白代码在执行前会预编译,然后再执行是关键。
     outerFun();     
     alert(a);
       上面这两行代码在运行之前,js的后台编译器会干下面的事情:

        编译器会先看看在全局代码中有没有 var关键字定义的变量和函数声明 , 我们这个例子中是有的,所以就在全局对象(也就是window对象)添加上 相应的属性,具体到我们的例子,现在的全局对象就是下面的样子(我觉得这样写大家肯定能看懂,我第一次看到都懂了)
  备注:( vo : Variable Object)   活动对象
    globalContext.VO(这个就是全局对象的意思) = {
          a: undefined 
        outerFun: <reference to  function  这里是对象的意思 >
};
         记住,在预编译阶段,所有的var关键字定义的变量,都被赋值:undefined 

     然后,把这个 globalContext.VO存入到函数 outerFun的内部属性中去,这个内部属性就叫做:[[scope]]

    预编译就结束了,就开始执行了。
     毫无疑问,执行的时候,会从上到下执行吧,所以
      var a=4  第一个被执行,发生标识符解析,因为这个代码是在全局环境中,所以就到全局对象中找下有没有a这个符号,
      发现globalContext.VO中有个叫 a的属性,在预编译的时候 globalContext.VO.a=undefined,所以 马上执行,             globalContext.VO.a变成了4.

    然后就轮到函数 outerFun 执行了,好,让人糊涂的事情又来啦。
     在执行outerFun() 时,js会为这段代码创建一个 :  执行环境 (也叫执行上下文),然后函数outerFun中的代码就在这个执行环境中被执行。 这个执行环境在被创建时,会发生下面的事情:
      一个叫做  活动对象(简称:AO) 的 家伙被建立了,这个家伙比较特殊,它拥有可访问的命名属性,但是它又不像正常对象那样具有原型(至少没有预定义的原型),而且不能通过 JavaScript 代码直接引用活动对象。
     这个活动对象会看看函数outerFun 里面有没有下面这3样东西:
    1  var关键字定义的变量
    2  函数声明
    3  函数形式参数
    我们的例子中,是有var关键字定义的变量的,所以它会给自己添加个属性a,然后同样赋值:undefined 。
    可以这样理解:   AO.a=undefined.
     然后就会为函数outerFun 的执行环境分配作用域链,
      这个作用域链是这个样子的:outerFun.AO——outerFun的[[scope]]  
     意思就是:outerFun函数的活动对象在最前面,然后就是outerFun函数在被定义时保持在它内部的 [[scope]]  属性。
      这也是我们老在网上看到的,javascript权威指南中老说的一句话:”JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.” 
函数 outerFun的 [[scope]] 属性在预编译的时候就填入好了嘛,后面不管outerFun在哪里运行(调用),这个 [[scope]] 属性都不会变。

      呼~~  准备工作终于全部完毕了,就开始正式执行代码内啦!
        首先执行这句  var a =0; 发生标识符解析, 

       js会首先在outerFun函数的活动对象中看看有没有a这个符号,如果没有,就到outerFun的 [[scope]]  中去找,outerFun的 [[scope]]  中又存入的是它定义时的  globalContext.VO,
所以在目前的情况下 这个标识符解析查找顺序就是   outerFun.AO——globalContext.VO

很显然,a符号在outerFun.AO 就被找到了,所以a立刻被赋值为0 ,变成这个样子:outerFun.AO .a=0;
       然后就执行alert(a),a标识符被解析,同样执行一遍查找:outerFun.AO——globalContext.VO
        outerFun.AO中找到a,值为0
         所以 alert(a)会弹出0.
        outerFun 函数就执行完了,然后执行 outerFun()后面的那句 alert(a)
        a标识符解析,因为句代码是定义在全局环境中的,同理,a符号只能在globalContext.VO中找吧,
       找到了,globalContext.VO.a=4.
       所以这个alert(a)就弹出4.

       ok 原理就是这样,下面来大量的看例子吧。

     function outerFun()
{
//没有var 
a =0;
alert(a); 
}
var a=4;
outerFun();   //0
alert(a);      //0


  同样,先预编译,globalContext.VO和上面的一摸一样。
  预编译完毕,执行var a=4,标识符解析,在globalContext.VO中找到a,执行赋值,完毕后:globalContext.VO.a=4
  执行函数  outerFun(),创建执行环境,分配作用域链(同样还是outerFun.AO——globalContext.VO)
  前面说了,函数  outerFun 的活动对象会在它自己内部查看3样东西:
1  var关键字定义的变量
    2  函数声明
    3  函数形式参数

这里都没有吧!!(a =0  这里的a没有用var定义,所以outerFun.AO中就没有a这个属性了)

    然后开始 执行代码:a =0;标识符解析:先在outerFun.AO中找,没找到,就跑到globalContext.VO中找,找到了,发现之前有个值是4, 然后就修改globalContext.VO的值,变成这样:globalContext.VO.a=0;

   这里非常重要,解释了为啥在函数内部定义的变量不用var 关键字定义就是全局变量的意思,而且还会修改到全局变量中同样名字的属性值。

     然后执行代码:alert(a),标识符解析:outerFun.AO中没有,globalContext.VO中找到,值为0 
    所以弹出0
    outerFun函数执行完毕,执行下面的alert(a) 标识符解析:同理,代码定义在全局环境中,只能在globalContext.VO中找,找到 值为0, 弹出0

         先写到这,分析的肯定是透彻的,所以长了点。
         看看大家反应如何!!!  后面的帖子好继续
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值