几乎所有的编程语言都可以存储,访问,修改变量,那在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. Android零基础入门第12节:熟悉Android Studio界面,开始装逼卖萌

    原文:Android零基础入门第12节:熟悉Android Studio界面,开始装逼卖萌 通过前两期的学习,我们可以正确搭建好Android Studio的开发环境,也创建了HelloWorld工程 ...

  2. win32界面程序开发,自制一个会自动销毁的提示框

    思路:创建线程里面启动窗口,设置定时器关闭该窗口. 创建窗口几个步骤和一般窗口无异,注册.然后createwindow,之后showwinodw,然后消息循环. 在createwindow之后,获取到 ...

  3. 通过SSIS的“查找”组件进行不同数据源之间数据的合并操作

    原文:通过SSIS的"查找"组件进行不同数据源之间数据的合并操作 为了协助开发还原生产环境中的某些bug,需要将将生产环境的某些特定表数据导入到测试环境做测试,之前一直都是暴力地t ...

  4. PRML Chapter3

    曲线拟合的几种方法 最大似然估计MLE,最大后验概率MAP:MLE和MAP MLE 给定一堆数据,假如我们知道它是从某一种分布中随机取出来的,可是我们并不知道这个分布具体的参,即"模型已定, ...

  5. ASP.NET WebForm项目--页面正确返回404及500等状态

    实现方法 项目路径 <system.webServer> <httpErrors errorMode="Custom" > <remove statu ...

  6. 【转】解决Nginx php-fpm配置有误引起的502错误

    转自:https://www.centos.bz/2017/07/nginx-php-fpm-502-error/ 在Ubuntu+Nginx+PHP环境下部署好以后,访问网站报错502,在后台ngi ...

  7. 创建一个简单的Django项目

    1.首先,启动pycharm,点击File->New Project,如下图所示. 2.在New Project对话框中,选择Django,在Location中设置项目路径以及项目名称,在App ...

  8. kali 开启键盘背光灯 && 自定义开启终端

    新建文件保存扩展名为.sh 编辑文件,键入如下代码 #!/bin/bash - status=/tmp/keyboard_led_status if [[ ${1} == "on" ...

  9. 直播技术:从性能参数到业务大数据,浅谈直播CDN服务监控

    线上服务的有效监控和数据收集,一直是后端服务离不开的话题.直播作为一种经典的分布式系统,监控以及数据收集更是必不可少的工作.如何对海量的服务集群有效的监控和保活,又如何抓取集群中的碎片数据中来优化服务 ...

  10. 经典Java笔试面试题

    面向对象编程(OOP) Java是一个支持并发.基于类和面向对象的计算机编程语言.下面列出了面向对象软件开发的优点: 代码开发模块化,更易维护和修改. 代码复用. 增强代码的可靠性和灵活性. 增加代码 ...