zoukankan      html  css  js  c++  java
  • JavaScript谁动了你的代码

      到目前为止,同学你知道了JavaScript的历史,也了解其“你想是啥就是啥”的变量系统。相信凭借你深厚的Java或者C++功底,再加上程序员特有的自傲气质,你肯定会信心满满:自信写JavaScript毫无压力。我也相信写个Script对于后端攻城师们那肯定不在话下。但是,当结果匪夷所想的时候,你或许会一番吐槽:真TM见鬼了,会不会是什么bug?还是浏览器有问题?我的代码逻辑没问题啊......。就像如下代码,你能说出结果是什么吗? 

    var a=123;
    var b=999;
    function func(a){
        var b;
      console.log(a);//?????? 结果是什么????留着分析
      var a=888;
      c=1111;function a(){
          }
        console.log(a);//?????? 结果是什么????留着分析
      console.log(b);//?????? 结果是什么????留着分析
      console.log(c);//?????? 结果是什么????留着分析
    }
    func(456); 

      是的,你的代码没有问题,当然浏览器也没问题。你或许说我才不会写得满屏都是“a”的代码!讲真的,当你看到这段代码的时候,你有没有想过为什么JavaScript能够这样重复定义同名的变量?本楼敢打赌十个看客中,能有一个提出这个疑问,那已经是惊喜了。可能有人会说“因为它是弱类型言语”,这个答案只能说对了一半。这看似很不科学、很不严谨的变量定义,怎么能够运行起来呢?很明显不科学。答案是:有人动了你的代码!

      有人动了你的代码!有人动了你的代码!有人动了你的代码!重要的事说三遍!那是谁动了你的代码呢?故事又开始了。

      这事还得回到九十年代JavaScript出生那会。话说布兰登-艾奇当时创造JavaScript的时候,他的需求就是做做客户端的数据验证而已。于是乎,他想“这玩儿没必要搞高能设计,看上去好像也没有什么地方需要高能运算的,搞预编译、链接器那是太浪费了,再说这玩儿是在浏览器上跑的,搞编译器、链接器,那浏览器不成了IDE啦?最好能像Perl那样,边解析边运行最美不过”。鞋同们看到这里应该明白了:那么多废话,你就不是为了说JavaScript是边解析边运行的嘛!我懂的,这个课本上有说。但是好多课本好像只说了边解析边运行,但是没说是怎么解析的,就算有说了,那也是废话比这篇博文还多,还说不清楚。到此,前面高呼三声那个问题的答案,想必看官到此也看出答案了:解析器动了你的代码!

      解析器动了你的代码!那得先认认真真说下“从你敲下代码,然后运行,最后输出结果”这个过程到底发生了什么?课本都说了“边解析、边运行”,毫无疑问这个过程就分为“解析期”与“运行期”。那下面我们就以上面的代码为例,看看你的代码是怎么被动了手脚后再运行的。

    解析期

      先照本宣科说说楼主对解析期的理解:解析期就是每一个运行单元在代码运行前,解析器对用户代码(程序员写的代码)进行解析调整的时期。这里有个关键的术语“运行单元”。什么是运行单元?这里仅以浏览器环境做说明(nodejs环境可能不一样)。简单地理解,一个页面是一个运行单元,一个function也是一个运行单元。一个页面的JavaScript在运行前,页面的所有JavaScript声明定义都被解析调整一遍;在一个function在运行前,这个function内的所有JavaScript声明定义(包括形参)都被解析调整了一遍。看了本楼的个人见解(如有误,请斧正),你或许会问:按你的意思页面加载完成的时候,先解析了一次页面上的JavaScript,之后在调用function的时候又进行了一次解析,那岂不是有n次解析?对!没错,有n次解析!鞋同你看准了,楼主特意高亮的【JavaScript声明定义。那什么是声明定义呢?且看代码:

         var a;//是声明定义        
            var a=123;//包含了声明定义、赋值运算表达式
            function f(){//是一个function定义
            }
            var f=function(){//包含了声明定义、function赋值运算表达式
            }

      看官要是有耐心看到这里,你应该明白了什么是解析期,也了解了什么是JavaScript声明定义。本楼再次强调“解析器只对声明定义”进行解析调整,像上面的“var a=123”、“var f=function(){}”会被拆为两部分,声明定义及赋值运算!声明定义用于解析期,赋值运行用于运行期。那解析器是怎么解析调整JavaScript的声明定义的呢?下面以博文第一段给出疑问的示列代码func函数做分布分析。

      第一步:JavaScript运行时,发现准备要调用func(456)

      第二步:func是一个函数执行单元,在执行前,需要解析调整

      第三步:为func执行单元准备一个当前的ActivityObject活动对象,即在func执行单元内生成一个所谓的活动对象,伪代码为:var AO={};

      第四步:先解析func形参定义,发现func定义了一个形参a,那么将a挂到AO对象上,并且将实参赋给形参,AO={a:456}

      第五步:解析变量声明定义,发现定义了var b,AO={a:456,b:undefined}

      第六步:解析变量声明定义var a=888,拆分为var a;a=888;发现AO中已经有了a定义,不做调整,AO={a:456,b:undefined}

      第七步:解析函数定义,发现function a(){}函数定义,AO={a:function(){},b:undefined}

      怎么样!看官,知道解析器是怎么动了你的代码吧。你写的所有声明定义都被移动到了一个活动对象上!请记住,解析器是这样动你的代码的:准备活动对象,然后解析形参而且进行实参赋值,然后解析函数内的var 变量声明定义(如果包含赋值则拆分赋值运算)、然后再解析函数定义。 

      到目前为止,解析器偷梁换柱的工作做完了,一切就绪,只欠Running!那Running什么?剩下的那些代码就是Running的,如var a=888、c=111、console.log()。就是运行期里面要发生的事情。那接下来,说说运行期的事情,结果便会分晓!

    运行期

      运行期,那就是直接跑代码咯,没什么定义好说的。但是这个运行期还有个令人惊讶的地方。这家伙每遇到一个变量(包括函数变量),都会先从当前的ActivityObject中查找是否存在,如果不存在则往上查找(作用链?原型链?这里预留下一篇博文)。这个奇怪的行为就造成了前面博文提到的神奇的变量提升作用。看官,你终于知道什么是变量提升了吧,也知道变量提升是什么鬼造成的了吧!好!废话少说,咱们还是规矩分析下运行期是怎么跑代码的。

      第一步:运行console.log(a),找AO对象,发现a=function,所以第一个结果是function(){}

      第二步:运行var a=888,找AO对象,发现有个a定义,执行赋值运算,此时AO={a:888,b:undefined},函数被覆盖了! 

      第三步:运行c=1111,找AO对象,没货!往上找,还是没货,好吧,到处没货,那只能留给父亲大人了,于是c变成了父亲大人的成员,并赋值为1111

      第四步:运行console.log(a),找AO对象,发现有料,a=888,结果是888

      第五步:运行console.log(b),找AO对象,发现有料,b=undefined,结果undefined,特别声明:undefined和xxx is not defined是两回事!

      第六步:运行console.log(c),找AO对象,没货,找父亲大人的,发现父亲大人有个c=1111,结果是1111


      各位看官,时间不早了,看看写得也差不多了。看完这篇博客,你应该知道了咱们写的代码是被动过后,再运行的。








  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 数字黑洞
    Minifilter微过滤框架:框架介绍以及驱动层和应用层的通讯
  • 原文地址:https://www.cnblogs.com/kevinJhuang/p/6124040.html
Copyright © 2011-2022 走看看