几乎所有的编程语言都可以存储,访问,修改变量,那在JavaScript中这些变量放在那里?程序如何找到他们?

js被归类于解释执行语言,但事实上他也是一门编译语言,因为他也要编译,但于传统的编译语言不同,他不是提前编译,编译结果也不能在分布式系统中进行移植。但js引擎编译的步骤和传统的编译语言非常相似。

传统的编译会经历3个步骤:

  1. 分词:将组成的字符串分解成有意义的代码块(词法单元)for instance:var a = 2;被分解成var,a,=,2,;。
  2. 语法分析:将词法单元转换成有元素逐级嵌套所组成的代表了程序语法结构的树,这个树叫抽象语法树。
  3. 代码生成:将抽象语法树转换成可执行的代码的过程。简单来说就是将抽象语法树转化为一组机器指令,用来创建一个叫作a的变量(包括分配内存等),并将值存储到a中。

对于传统的编译过程,js引擎要复杂的多,js编译发生在执行前的几微秒,然后做好执行他的准备,并且通常马上就会执行他。

说道这还是没有说这些变量放在那里?下面介绍3位大佬:

  1. 引擎:从头到尾负责整个js程序的变以及执行过程。
  2. 编译器:负责语法分析和代码生成等。
  3. 作用域:负责收集并维护由所有声明的标识符(变量和函数)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些变量的访问权限。

这时候答案就浮出水面。啊,是这伙计--作用域.

作用域?大哥你哪来的啊?上面提到作用域像一个容器一样收集并维护所有标识符(变量和函数)事实上他是一个对象,收集的东西挂在它的属性上。作用域的出生是因为函数的产生而产生的。当函数执行的前一刻的时候,会创建一个作用域,这个作用域定义了这个函数执行时的环境,函数每次执行时的作用域都是独一无二的,因为他是对象啊,就像出生的孩子长得都不太一样。这个作用域小名特别多,什么执行期上下文,AO(Activation Object)对象,活动对象。作用域这帮伙计们有个头叫--全局作用域。里面的作用域能看到外面的,哈哈,你在我面前就是个小透明,但外的作用域可看不到里面的。

在js高程里解释道作用域。

作用域是因函数产生而产生的,每个对象都有属性和方法,函数(function)也是一种特殊的对象,函数可以有test.name test.prototype ...这些是可以访问的,还有一些属性是不可以访问的隐式属性仅供JavaScript引擎处理。 比如[[scope]]:指的是作用域链,其中存储了执行期上下文的集合。这个集合呈现链式连接,我们把这种连接叫做作用域链。作用域链本质上是一个指向变量对象的指针列表,他只是引用,但不包含实际变量对象。.[[scope]]这里面存的就是作用域。系统会根据内部的原理去定期调用scope。当函数执行的前一刻的时候,会创建一个称为执行期上下文的内部对象(AO activation object)。一个执行期上下文定义了一个函数执行时的环境。函数每次执行时对应的上下文都是独一无二的,即使执行一样的函数但是执行期上下文并不相同,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,他所产生的执行上下文会销毁。

上面还提到了编译,说道js编译发生在执行前的几微秒。也讲到变量和函数会放到作用域的问题,事实上,是这样的,在程序执行前不是要进行编译的吗,在引擎和编译器两位大哥的帮助下 ,在编译时(函数执行前)会处理声明的变量和函数,把他挂到作用域这个对象的属性上。这个过程叫 -- 见下行。

预编译

当你看见var a = 2;这段程序时,很可能认为这是一句声明,事实上我们引擎这哥们认为有两个完全不同的声明,一个由编译器在编译时处理,另一个在引擎运行时处理。

代码执行前会对其进行编译,首先编译器会分词,然后解析成语法树,最后进行代码生成,别忘了代码生成就是将语法树转化为一组机器指令。

  1. 生成代码前编译器会询问作用域是否有该名称的变量,如果有,忽略该声明,如果没有,会要求作用域在当前作用域的集合中声明一个新变量,并命名为a。
  2. 接下来为引擎生成运行时所需要的代码,这些代码用来处理a = 2这个赋值操作,运行时引擎首先会问作用域,在当前作用域集合中是否有一个叫作a的变量,如果找到就会对他赋值,否则就会抛出异常。

也就是说变量和函数在内的所有声明都会在任何代码被执行前首先被处理。--先编译,在执行。

console.log(a);  //undefinde
var a = 2;
console.log(a); //

这也解释了为什么第一行没有报错的原因。

首先代码执行前一刻进行编译,var a; console.log(a); a = 2; console.log(a);类似这样的执行顺序(就好像变量和函数从他们的代码中出现的位置被移动到了最上面)。 编译器看到var a会查看当前作用域是否有变量a,没有声明一个变量a,开始执行代码(js是顺序执行)。

  1. console.log(a)查看作用域是否有变量a,有,但没有值,a为undefined。
  2. var a = 2;查看作用域是否有变量a,有,对a赋值a = 2。
  3. console.log(a)查看作用域是否有变量a,有,值为2。

当函数和变量同名时,函数会覆盖变量。

  由此预编译过程可以总结成一下过程

  预编译四部曲:

  • 1.创建AO对象/活动对象(activation object)(执行期上下文)
  • 2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
  • 3.将实参值和形参统一
  • 4.在函数体里面找到函数声明,值赋予函数体

JavaScript作用域及预编译的更多相关文章

  1. JavaScript作用域原理——预编译

    JavaScript是一种脚本语言, 它的执行过程, 是一种翻译执行的过程.并且JavaScript是有预编译过程的,在执行每一段脚本代码之前, 都会首先处理var关键字和function定义式(函数 ...

  2. javaScript语言的预编译与运行

    JS代码执行的过程: 1.预编译 ---- 事先对js代码做一个预处理 2.代码运行---开始执行JS代码. JS编程: 1.加载DOM的最好在/BODY之前 2.与DOM渲染无关的放在Head里面 ...

  3. javascript中的预编译问题

    Js作为脚本语言,可以不需要编译直接运行,但遇到类似变量或者函数同名,预编译方面的知识可以帮助我们更好解决问题. 示例: 这是一段js中普通的函数调用代码 <script>1.    // ...

  4. JS作用域和预编译(转载 学习中。。。)

    JS在页面加载过程中顺序执行.但是分块预编译.执行. JS在执行前会进行类似”预编译”的操作,而且先预声明变量再预定义函数. 此时注意,是声明,不是定义,如:var a = 1; 在预编译中,只是执行 ...

  5. javascript作用域、预解析笔记

    1.作用域     一般情况下,一段代码中所用到的名字并不总是有效可用的,     而限定这个名字(变量)的可用性的代码范围就是这个名字的作用域,可用有效的减少变量名冲突     2.js的作用域(e ...

  6. JavaScript作用域原理(二)——预编译

    JavaScript是一种脚本语言, 它的执行过程, 是一种翻译执行的过程.并且JavaScript是有预编译过程的,在执行每一段脚本代码之前, 都会首先处理var关键字和function定义式(函数 ...

  7. 一步一步的理解javascript的预编译

    首先,我们要知道javascript是单线程.解释性语言.所谓解释性语言,就是翻译一句执行一句.而不是通篇编译成一个文件再去执行. 其实这么说还没有这么直观,读一句执行一句那是到最后的事了.到JS执行 ...

  8. 关于Javascript作用域及作用域链的总结

    本文是根据以下文章以及<Javascript高级程序设计(第三版)>第四章相关内容总结的. 1.Javascript作用域原理,地址:http://www.laruence.com/200 ...

  9. js中的预编译

    预编译 js执行顺序: 词法/语法分析 预编译 解释执行 js中存在预编译 function demo() { console.log('I am demo'); } demo(); //I am d ...

随机推荐

  1. 【转】编程之道 之 Rob Pike

    1.你无法断定程序会在什么地方耗费运行时间.瓶颈经常出现在想不到的地方,所以别急于胡乱找个地方改代码,除非你已经证实那儿就是瓶颈所在. 2.估量.在你没对代码进行估量,特别是没找到最耗时的那部分之前, ...

  2. Linux中的进程

    进程,线程,程序 通俗的说,进程是程序的一次执行过程,程序是一种静态概念,如果在系统中引入线程,则进程是资源分配单元,线程是系统执行单元.此处不懂应参阅<操作系统> 进程衍生 fork-e ...

  3. QT 线程池 + TCP 小试(三)实现最终功能

    *免分资源链接点击打开链接http://download.csdn.net/detail/goldenhawking/4492378 有了TCP.线程池,我们就可以把他们连接起来.使用最简单的 QMa ...

  4. Delphi 项目失败的总结

    随着项目的失败,这些天一直在总结失败的原因,到底是为什么? 一.技术层面        1.少用指针类型,多用类.            虽然指针类型能有效的节约内存和加快运行速度,但指针远没有类来得 ...

  5. js打印指定元素内容

    var v = document.createElement("div"); //向v中追加打印数据,可以将界面的元素追加进来 var h = window.open(" ...

  6. ASP.NET vNext 微笔记

    关心 ASP.NET vNext 的人可能已经读过相关文章,例如:ASP.NET vNext @ 2014.那么,你可能已经知道,ASP.NET vNext 摆脱了 System.Web.DLL,把 ...

  7. Redis EXISTS命令耗时过长case排查

    一.背景 redis慢日志分析平台上线后,随便看了一下,发现onestore使用的缓存集群,存在大量的EXISTS命令慢查询的情况: 平均每个EXISTS命令需要13ms,最大耗时近20ms.这个结果 ...

  8. Md2All:好用的markdown文件转换工具,文章迁移微信公众号的利器

    目录 简介 使用体验 极速上手 更多功能 总结 简介 markdown以简单的语法和强大的功能,征服了无数技术创作者,几乎主流的技术博客网站都开始支持markdown语言撰写博客.但是微信公众号的文章 ...

  9. spring boot之security

    上一节的时候,我们打开了springboot的端点,有一些数据是非常敏感的,比如/shutdown. 这一节,我们要给一些敏感信息加上权限控制. spring boot本身的security模块就很好 ...

  10. hadoop之hive集合数据类型

    除了string,boolean,date等基本数据类型之外,hive还支持三种高级数据类型: 1.ARRAY ARRAY类型是由一系列相同数据类型的元素组成,这些元素可以通过下标来访问.比如有一个A ...