JavaScript谁动了你的代码

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

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可以这样重复定义同名的变量?本楼敢打赌十个看客中,能有一个提出这个疑问,那已是惊喜了。可能有人会说“由于它是弱类型言语”,这个答案只能说对了一半。这看似很不科学、很不严谨的变量定义,怎么可以运行起来呢?很明显不科学。答案是:有人动了你的代码!java

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

  这事还得回到九十年代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函数作分布分析。spa

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

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

  第三步:为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=88八、c=1十一、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


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

相关文章
相关标签/搜索