几乎所有的编程语言都可以存储,访问,修改变量,那在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. 零元学Expression Blend 4 - Chapter 40 Flash做的到的Blend也可以!轻松制作拥有动画的MenuBar!(上)

    原文:零元学Expression Blend 4 - Chapter 40 Flash做的到的Blend也可以!轻松制作拥有动画的MenuBar!(上) 一直以来都有人拿Flash的动画问我Blend ...

  2. duilib菜单开发遇见“0xC0000005: 读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突”

    我的程序是这样一个逻辑. 首先创建用户列表,点击列表项弹出菜单,点击菜单上“设备选项”,弹出设备列表,上面显示这个用户拥有的设备. 菜单的创建参考了这为博主的教程:http://www.cnblogs ...

  3. 反射:获取枚举类型的Name,Value,Description

    [Obsolete("请使用新的方法XXX")] //使用Obsolete特性来告诉使用者这是一个过期的方法 private static void Test() { Type t ...

  4. SQL语法详解

    ALTER DATABASE修改数据库全局特性 ALTER DATABASE实际上是修改数据库目录中的dp.opt文件 ALTER TABLE修改表的结构 ALTER TABLE对表进行增删列,创建取 ...

  5. Setting up multi nodes live migration in Openstack Juno with devstack

    Setting up multi nodes live migration in Openstack Juno with devstack Summary Live migration overvie ...

  6. Windows系统版本判定那些事儿(有图,各种情况,很清楚)

    前言 本文并不是讨论Windows操作系统的版本来历和特点,也不是讨论为什么没有Win9,而是从程序员角度讨论下Windows获取系统版本的方法和遇到的一些问题.在Win8和Win10出来之后,在获取 ...

  7. 发现 TSplitter 在嵌套时不好用, 索性写了个替代品(处理MouseDown,MouseMove,MouseUp,然后设定控件的Left值就可以了)

    代替 TSplitter 的 TDirPanel 类: unit DirPanel; interface uses   Classes, Controls, Forms, ExtCtrls; type ...

  8. Qt5.4.2Mingw编译配置opencv2.4.9

    1 下载所需工具 (1)qt-opensource-windows-x86-mingw491_opengl-5.4.2.exe  842M 下载地址https://download.qt.io/arc ...

  9. 核心思想:评价早期SaaS创业公司时,投资人在关注什么?(是否有机会发展成一个平台,长期的护城河)

    编者按: 当聊到早期项目时,人们经常会问投资人一个问题:“在评价早期 SaaS 创业公司时,投资人会关注什么——指标还是其他方面?” Nakul Mandan 作为 Lightspeed 风投机构的合 ...

  10. 如何保证MQ消息必达

    此文章属于笔记,原属58沈剑 一.MQ消息必达,架构上的两个核心设计点: 消息落地 消息超时.重传.确认 四大部件:发送端 接收端 服务端 固化存储组成 二.上半场消息必达以及消息重复问题 上半场的流 ...