几乎所有的编程语言都可以存储,访问,修改变量,那在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. Linux简单文本处理

    tr命令:tr [option] set1 [set2] 删除或者替换set1中的字符在文本表示这个问题中,windows系统下,\r\n为换行:而linux系统下,\n为换行.win->lin ...

  2. Docker Explanation and Apache Image

    https://blog.sajjan.com.np/2017/02/05/docker-getting-started-containers-ubuntu/ https://blog.sajjan. ...

  3. SSL Converter & Formats

    https://www.sslshopper.com/ssl-converter.html PEM Format The PEM format is the most common format th ...

  4. delphi 实现微信开发(1) (使用kbmmw web server)

    原文地址:delphi 实现微信开发(1)作者:红鱼儿 大体思路: 1.用户向服务号发消息,(这里可以是个菜单项,也可以是一个关键词,如:注册会员.) 2.kbmmw web server收到消息,生 ...

  5. Java的Qt绑定 jambi

    大二在学java,所以有时会写点java的小程序,可是习惯了qt的界面,使用AWT和swing让我有些不适,后来发现了jambi,才知道原来早就有了java的绑定版,所以迫不及待的安装了上.      ...

  6. Linux软件安装及基本概念

    apt 基本用法 apt-get [options] install/remove/source 软件包1 [软件包2...] 注意:软件包不要带后缀.deb 常用命令及解释如下: apt下载软件是根 ...

  7. Windows Phone8.1系统新特性

    Windows Phone 8.1 beta SDK已经为大家透露了不少WP8.1系统的新特性,不过这些新特性还不能保证在最终的消费者版本中都有所体现,毕竟它还仅是SDK版本.日前,国外媒体WPCen ...

  8. SilverlightMVVM模式中的数据校验

    silverlight的数据校验大体分成3种类型: 数据是非必填的但是需要满足相应数据格式的 数据是必填的且可能需要进行数据格式校验的 其他(如数据的联动校验) 以下的数据校验方式针对第二种: 在相应 ...

  9. mysql查询类型转换问题

    mysql转换类型.类型转换.查询结果类型转换 一.问题来源 数据库一张表的主键id设为了自增,那就是int型的,但是其他表的关联字段又设置成了字符串! 而且已经开发了很久才发现问题,既然出现了问题肯 ...

  10. pomelo使用中的常见问题

    1.端口被占用, 有进程没杀干净. 用 pomelo kill --force 命令清一下进程.