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

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

  是的,你的代码没有问题,当然浏览器也没问题。你或许说我才不会写得满屏都是“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

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

JavaScript谁动了你的代码的更多相关文章

  1. 【JAVA系列】使用JavaScript实现网站访问次数统计代码

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[JAVA系列]使用JavaScript实现网站 ...

  2. html+css+javascript实现列表循环滚动示例代码

    使用html+css+javascript实现列表循环滚动,设置时间定时,在规定的时间内替换前一个节点的内容,具体示例如下,感兴趣的朋友可以参考下 说明:设置时间定时,在规定的时间内替换前一个节点的内 ...

  3. Javascript 拖拽雏形——逐行分析代码,让你轻松了解拖拽的原理

    拖拽的原理: 其实就是鼠标与左上角的距离保持不变.我们来看下图, 这红点就是鼠标. 拖拽拖拽实际上来说就是通过鼠标的位置来计算物体的位置,就是这么简单,就是这么任性. 那这个距离怎么求呢?? 鼠标的位 ...

  4. Javascript 多物体运动——逐行分析代码,让你轻松了解运动的原理

    我们先来看下之前的运动的代码,是否支持多物体运动,会出现怎么样的问题. <style type="text/css"> div { width: 100px; heig ...

  5. 原生javascript封装动画库

    ****转载自自己发表于牛人部落专栏的文章**** 一.前言 本文记录了自己利用原生javascript构建自己的动画库的过程,在不断改进的过程中,实现以下动画效果: 针对同一个dom元素上相继发生的 ...

  6. gRaphael——JavaScript 矢量图表库:两行代码实现精美图表

    gRaphael 是一个致力于帮助开发人员在网页中绘制各种精美图表的 Javascript 库,基于强大的 Raphael 矢量图形库.你只需要编写几行简单的代码就能创建出精美的条形图.饼图.点图和曲 ...

  7. JavaScript 使用反斜杠对代码行进行折行

    JavaScript对代码行进行折行-使用反斜杠 您可以在文本字符串中使用反斜杠对代码行进行换行.下面的例子会正确地显示: <html> <head> <script t ...

  8. 《JavaScript模式》第6章 代码复用模式

    @by Ruth92(转载请注明出处) 第6章:代码复用模式 GoF 在其著作中提出的有关创建对象的建议原则: -- 优先使用对象组合,而不是类继承. 传统模式:使用类继承: 现代模式:"类 ...

  9. javascript——拖拽(完整兼容代码)

    拖拽,是JS经常会用到的效果,在网上有很多的这样那样的拖拽效果,但其中往往大多有各种各养的问题,功能不全,无法兼容,而且修改的时候 也是十分麻烦. 其实拖拽的原理很简单,无非是鼠标的三个动作的解析,以 ...

随机推荐

  1. SQL0668N 由于表 "db2inst1.test" 上的原因代码 "3",所以不允许操作(解因为LOAD引起的LOAD暂挂状态锁)

    DB2解因为LOAD引起的LOAD暂挂状态锁   一般解锁命名是,SET INTEGRITY FOR temp_test IMMEDIATE CHECKED   但是load暂挂状态是解不了的,可以l ...

  2. N元数组的子数组之和的最大值

    题目:有N个整数的元素的一维数组,求子数组中元素之和中最大的一组(思想:动态规划) 分析: 设该数组为array[N], 那么对于array[i]该不该在元素之和最大的那个子数组中呢?首先,不如假设a ...

  3. Android FrameWork——相关内容

    http://blog.csdn.net/stonecao/article/category/781516

  4. leetcode—jump game

    1.题目描述 Given an array of non-negative integers, you are initially positioned at the first index of t ...

  5. Web服务器与Servlet容器

    今日要闻: Oracle启动了JRE7到JRE8的自动更新, JRE8发布于2014.3,于2014.10成为java.com默认版本, JRE7发布于2011.7, Oracle指定的Java生命政 ...

  6. ubuntu14.04修复启动项

    自从安装了ubuntu14.04系统后(win7+ubuntu双系统),一直使用grub来引导win7和ubuntu,很长一段时间都使用的很好.突然前两天win7进不去了,无奈之下就用pe修复了win ...

  7. 使用Go开发一个简单的服务器程序

    最近有个小项目,需要一个简单的后台程序来支撑,本来想用Nodejs来做,但是由于本人js一直很菜,并且很讨厌callback,虽然我也很喜欢异步模型,但我一直都觉得JS是反人类的.后台就用了go处理, ...

  8. Python程序的混淆和加密

    混淆 为了增加代码阅读的难度, 源代码的混淆非常必要, 一个在线的Python代码混淆网站. 如果你觉得有用, 可以购买离线版本.同时需要注意的是, 这个混淆其实还是被很多人怀疑的, 因为即使混淆了, ...

  9. miracast 协议wifi display

    看wifi direct display标准的地方: http://www.wi-fi.org/discover-wi-fi/specifications Miracast依赖的Wi-Fi技术项[②] ...

  10. 数据库中使用 Synonym和openquery

    如果,你想在一台数据库服务器上,查询另一个台数据服务器的数据该如何做呢?如果,你想在同一台数据服务器上,在不同的数据库之间查询数据,又该怎么办呢?那就让我为你介绍Synonym和openquery吧. ...