-
2018-05-28 10:55:01
javaScript是一门编译型语言。
传统编译语言流程分为:分词/词法分析;解析语法分析;代码生成。
分词/词法分析:将字符串代码分解为有意义的代码块。这些代码块被称为词法单元。
解析/语法分析:将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树(抽象语法树)。
代码生成:将抽象语法树转换为可执行的代码的过程称为代码生成。
javaScript引擎:(负责整个javaScript的编译及执行)
编译不在构建之前,而在代码执行前的几微秒(甚至更短)的时间里,javaScript使用这种方式来保证性能最佳。
编译器:
负责词法分析及代码生成。
作用域:
收集并维护所有声明的标识符(变量),确定当前执行的代码对这些标识符的访问权限。
例:var num = 2
1.编译器询问作用域是否已存在变量num在当前作用域内,如果有则忽略声明,继续编译,如果没有,则作用域在当前作用域集合中声明一个变量num;
2.编译器为引擎生成运行所需代码。引擎询问当前作用域是否存在变量num,如果有则赋值,如果没有则继续寻找。如果最总找到了则会赋值,如若最终没有生成,则抛出异常。
总结:变量的赋值操作会执行两个操作,首先会在当前作用域中声明变量(如果之前没有声明过的话)。然后引擎在当前作用域中查找该变量,并赋值。
左查询/右查询:
左查询:赋值操作的目标是谁(赋值)。 右查询:谁是赋值操作的源头(取值)。
更多相关内容 -
JS学习系列 01 - 编译原理和作用域
2018-07-23 11:16:40原文: JS学习系列 01 - 编译原理和作用域 在学习 javascript 的过程中,我们第一步最应该了解和掌握的就是作用域,与之相关还有程序是怎么编译的,变量是怎么查找的,js 引擎是什么,引擎和作用域的关系又是...在学习 javascript 的过程中,我们第一步最应该了解和掌握的就是作用域,与之相关还有程序是怎么编译的,变量是怎么查找的,js 引擎是什么,引擎和作用域的关系又是什么,这些是 javascript 这门语言最基础的地基,至于对象、函数、闭包、原型链、作用域链以及设计模式等等都是地基以上的建筑,只有地基打牢了,建筑才会稳。同样只有先把最基础的部分掌握了,之后的扩展学习才会更容易。
这一节我要说的,就是作用域和编译原理,从这里开始,我会一点点的把深入学习 javascript 的过程中总结的知识点以及遇到的问题,一篇一篇的梳理出来,如果有志同道合的朋友,可以关注我这个系列,我们一起玩转 javascript。
1. 编译原理
大家通常把 javascript 归类为一种“动态”或“解释执行”的语言,但事实上,它是一门编译语言,但和传统的编译语言不同,它不是提前编译的,编译结果也不能进行移植。
在传统编译语言中,程序在执行之前会经历三个步骤,统称为“编译”:
- 分词/词法分析
这个过程会把字符串分解成有意义的代码块,这些代码块被称为词法单元。
例如 var a = 5; 这段程序通常会被分解成下面这些词法单元: var、a、=、5、; 。空格是否会被当成词法单元取决于空格在这门语言中是否有意义。 - 解析/语法分析
这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树。这个树被称为“抽象语法树”(Abstract Syntax Tree,AST)。
var a = 5; 的抽象语法树中可能如下图所示:抽象语法树
- 代码生成
将 AST 转换为可执行代码的过程被称为代码生成。这个过程与语言、目标平台等息息相关。简单来说,就是通过某种方法可以将 var a = 5; 的 AST 转化为一组机器指令,用来创建一个叫做 a 的变量(包括分配内存等),并将一个值 5 存储在 a 中。
比起那些编译过程只有三个步骤的语言的编译器来说,javascript 引擎要复杂的多。
例如,在词法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。首先我们要清楚,javaScript 引擎不会有太多的时间来进行优化(相对于其它语言的编译器来说),因为与其它语言不同,javascript 的编译过程不是发生在构建之前的。
对于 javascript 来说,大部分情况下编译发生在代码执行前的几微秒(甚至更短)的时间内。在我们将要讨论的作用域背后,javascript 引擎用尽了各种办法(比如 JIT,可以延迟编译甚至重新编译)来保证性能最佳。
总结来说,任何 javascript 代码片段在执行前都要进行编译(预编译)。因此,javascript 编译器首先会对 var a = 5; 这段程序进行编译,然后做好执行它的准备,并且通常马上就会执行它。
2. 三位好友
要真正理解作用域,我们首先要知道 javascript 中有三位好朋友:
- 引擎
从头到尾负责整个 javascript 程序的编译及执行过程。 - 编译器
负责语法分析及代码生成。 - 作用域
负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。
当遇见 var a = 5; 这一段代码时,其实执行了两个步骤:
(1)var a; 编译器会询问作用域是否已经有一个该名称的变量存在于同一作用域的集合中。如果是,编译器会忽略该声明,继续进行编译,否则它会要求在当前作用域的集合中声明一个新的变量,并命名为 a 。
(2)a = 5; 编译器会为引擎生成运行时所需的代码,这些代码用来处理 a = 5; 这个赋值操作。引擎运行时会首先询问作用域,在当前作用域的集合中是否存在一个叫作 a 的变量,如果是,引擎就会使用这个变量。如果否,引擎会继续向父级作用域中查找,直到找到全局作用域,如果在全局作用域中仍没有找到 a ,那么在非严格模式下,引擎会为全局对象新建一个属性 a ,并将其赋值为5,在严格模式下,引擎会报错误 ReferenceError: a is not defined。总结来说,变量的赋值会执行两个操作,首先编译器会在当前作用域声明一个变量(如果之前没有声明过),然后在运行时引擎会在当前作用域中查找该变量(找不到就向上一级作用域查找),如果能够找到就会对它赋值。
3. LHS 和 RHS
前面说到引擎在为变量赋值的时候会在作用域中查找变量,但是执行怎样的查找,用什么方式,会对最终的查找结果造成影响。
在 var a = 5; 这个例子中,引擎会对 a 进行 LHS 查询,当然,另外一个查找类型叫作 RHS。
对变量进行赋值所执行的查询叫 LHS(Left-hand Side)。
找到并使用变量值所执行的查询叫 RHS(Right-hand Side)。举个例子:
function foo(a) { // 这里隐式包含了 a = 2 这个赋值,所以对 a 进行了 LHS 查询 var b = a; // 这里对 a 进行了 RHS 查询,找到 a 的值,然后对 b 进行 LHS 查询,把 2 赋值给 b return a + b; // 这里包含了对 a 和 b 进行的 RHS 查询 } var c = foo(2); // 这里首先对 foo 进行 RHS 查询,找到它是一个函数,然后对 c 进行 LHS 查询把 foo 赋值给 c
所以上面的例子共包含 3 个 LHS 查询和 4 个 RHS 查询,你们都找对了吗?
4. 作用域嵌套
当一个块或函数嵌套在另一个块或函数中时,就发生了作用域嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止。
举个例子:
function foo(a) { console.log(a + b); } var b = 2; foo(2); // 4
这里对 b 进行的 RHS 查询在 foo 作用域中无法找到,但可以在上一级作用域(这个例子中就是全局作用域)中找到。
总结来说,遍历嵌套作用域链的规则很简单:引擎从当前执行的作用域中开始查找变量,如果都找不到,就向上一级继续查找。当抵达最外层的全局作用域时,无论找到还是没找到,查找过程都会停止。
- 分词/词法分析
-
JS作用域(1) - 编译原理
2021-11-11 20:33:15属于编译语言的JavaScript,编写的源代码在执行之前,会经历三个步骤,这个过程统称为“编译”。 三个步骤为:分词/词法分析 -> 解析/语法分析 -> 代码生成 第一步骤:分词/词法分析 分词:判断词法单元...属于编译语言的JavaScript,编写的源代码在执行之前,会经历三个步骤,这个过程统称为“编译”。
三个步骤为:分词/词法分析 -> 解析/语法分析 -> 代码生成
第一步骤:分词/词法分析
分词:判断词法单元时,调用的是无状态的解析规则
词法分析:判断词法单元时,调用的是有状态的解析规则
过程:由字符组成的字符串分解成有意义的代码块(词法单元)
例如: var a =2 ; => 词法单元: var | a | = | 2 | ;
第二步骤:解析/语法分析
过程:将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的抽象数据树(AST)
例如: var a =2 ;的抽象数据树
第三阶段:代码生成
过程:将AST转换为可执行代码的过程被称为代码生成
参与以上这个编译过程的有三个角色:引擎、编译器、作用域
角色一:引擎
工作职责:从头到尾负责整个JavaScript程序的编译及执行过程
执行查询类型:LHS 、RHS
当变量出在赋值操作的左侧时进行LHS查询,出现在右侧时进行RHS查询
赋值操作的目标是谁-LHS
谁是赋值操作的源头-RHS
例子:
function foo(a) { var b = a; return a + b; } var c = foo(2);
LHS: foo(a)中的a | var c = | var b =
RHS: = a | = foo(2) | return a + | return + b
引擎会在LHS和RHS的过程中,在作用域内寻找需要的变量和值
角色二:编译器
工作职责:负责语法分析及代码生成等活动
角色三:作用域
工作职责:负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确实当前执行的代码对这些标识符的访问权限
三个角色的协作过程:变量的赋值操作会执行两个动作
- 在编译时,编译器会在当前作用域中声明一个变量
- 在运行时,引擎会在作用域中查找该变量,如果能够找到就会对它赋值
-
作用域是什么?
2021-05-17 18:31:41作用域是什么? 简单的理解:作用域指的是一个变量的作用范围。 太简单了吧 几乎是所有编程语言最基本的功能之一,就是能够储存变量当中的值,并且可以在之后对这个值进行访问或修改。事实上,正是这种储存...作用域是什么?
-
简单的理解:作用域指的是一个变量的作用范围。 太简单了吧
-
几乎是所有编程语言最基本的功能之一,就是能够储存变量当中的值,并且可以在之后对这个值进行访问或修改。事实上,正是这种储存和访问变量的值的能力将状态带给了程序。
-
如果没有状态的这个概念,程序虽然也可以执行一些简单的任务,但是它会受到高度限制,做不到非常的有趣。
-
将变量引入程序会引起几个很有意思的问题,它们储存在哪里?最重要的是,当程序需要时如何才能找到它们?
-
以上的问题说明需要一套设计良好的规则来存储变量,并且在之后可以方便的找到这些变量。这套规则被称为作用域。
-
但是,究竟在哪些地方并且怎么样去设置这些作用域的规则呢?
1.编译原理
尽管我们通常将JavaScript归类为“动态“语言或“解释性执行”语言,但事实上它是一门编程语言。
它与传统的编程语言不同,它不是提前编译的,编译的结果也不能在分布式系统中进行移植。
JavaScript 引擎进行编译的步骤和传统的编译语言非常相似,在某些环节可能 比预想的要复杂。
-
JavaScript引擎不会有大量的(像其他语言编译器那么多的)时间用来进行优化,大部分情况下编译发生在代码执行前的几微秒(甚至更短的时间), 因为它与其他语言不同,JavaScript的编译过程不是发生在构造之前的。
-
简单来说,所有的JavaScript 代码片段在执行前都要进行编译(通常就在执行前)。因此, JavaScript 编译器首先会对 var a = 1; 这段程序进行编译,然后做好执行它的准备,并且 通常马上就会执行它。
2.理解作用域!
引擎 从头到尾负责整个 JavaScript 程序的编译及执行过程。 编译器 负责语法分析及代码生成等脏活累活。 作用域 负责收集并维护由所有声明的标识符(变量)组成的一系列查 询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限 -
var a=1; -当看到这段程序时,很可能认为这是一句声明,但引擎不这么看。事实上,引擎认为这里有两个完全不同的声明, 一个由编译器在编译时处理,另一个则由引擎在运行时处理。 -伪代码:声明一个变量,分配存储的空间,将其命名为a,然后将值1赋值给a并保存进这个变量。然而并不完全正确。 -事实上编译器会进行以下处理 1. 遇到 var a,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的 集合中。如果是存在,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作 用域的集合中声明一个新的变量,并命名为 a。 2. 接下来编译器会为引擎生成运行时所需的代码,这些代码被用来处理 a =1 这个赋值 操作。引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫作 a 的 变量。如果是,引擎就会使用这个变量;如果否,引擎会继续查找该变量。 如果引擎最终找到了 a 变量,就会将 1 赋值给它。如果引擎最终都没有找到就会举手示意 并抛出一个异常! 总结:变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如 果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对 它赋值。
在JavaScript中一共有俩种作用域 : 全局作用域 函数作用域(局部作用域)
1.全局作用域
-直接写在script标签内部的JavaScript代码
全局作用域在打开页面的时候创建,在页面关闭的时候销毁 全局作用域中有一个window对象,它表示的是一个浏览器的窗口,它由浏览器创建,可以直接使用 全局作用域创建的变量会作为window对象的属性保存 全局作用域中的函数都会作为window对象的方法保存
-全局作用域中的变量在页面的任意地方都是可以访问到的。
window.οnlοad=function(){ var a=5; fn1();//调用fn1(); var c=6; fn2();//调用fn2(); function fn1(){ var b=10; console.log(a+b);//15 } function fn2(){ console.log(c)//6 } }
2.函数作用域
当调用函数的时候创建函数作用域,函数执行完毕之后,作用域就会销毁。
每调用一次函数就会创建一个新的函数作用域,他们之间是相互独立的。
函数作用域中可以访问全局变量,但在全局作用域中无法访问到局部变量
var a=3; fn(); console.log(b);//b is not defined function fn(){ var b=2; console.log(a-b);//1 }
当函数作用域操作一个变量时,它会先在自身作用域中寻找, 如果有就直接使用,如果没有就会向上级作用域继续查找, 这样每次上升一级作用域,最后抵达全局作用域(顶层), 无论找到或者没有找到都将停止, 如果全局作用域中仍然没有找到, 则会报错ReferenceError
-
-
js底层原理作用域和作用域链
2019-07-31 22:05:181、作用域([[scope]]) [[scope]]:每一个js函数都是一个对象,对象中有些属性我们可以访问,但是有些不可以,这些属性仅供js引擎存取,[[scope]]就是其中的一个。[[scope]]指的是我们所说的作用域(作用域链),... -
JavaScript 详解预编译原理
2020-11-27 05:07:23JavaScript 预编译原理 今天用了大量时间复习了作用域、预编译等等知识 看了很多博文,翻开了以前看过的书(好像好多书都不会讲预编译) 发现当初觉得自己学的很明白,其实还是存在一些思维误区 (很多博文具有... -
编译原理之语义分析
2020-10-28 13:55:09作用域(Scope) 作用域是指计算机语言中变量、函数、类等起作用的范围 变量的作用域有大有小,外部变量在函数内可以访问,而函数中的本地变量,只有本地才可以访问。 变量的作用域,从声明以后开始。 在函数里,... -
编译原理 编译程序
2013-01-08 10:49:59递归下降的分析 C++ 实现 -
JavaScript进阶(二)词法作用域与作用域链实例分析
2020-11-21 06:49:56想了解更多关于作用域的问题推荐阅读《你不知道的JavaScript上卷》第一章(或第一部分),从编译原理的角度说明什么是作用域。概括的说作用域就是一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量。 ... -
什么是作用域及js编译原理
2016-12-31 22:41:15显然,我们必须定义一套规则来操作这些变量简单的进行读取和存储,而这套规则应该就是作用域了。来看一个简单的赋值语句var a=2; 大多数人会认为这是一个声明变量并赋值而已,事实也确实如此,而要强调的是电脑中的... -
编译原理
2021-06-07 06:49:04编译原理(计算机专业课程)编辑锁定讨论上传视频编译原理是计算机专业的一门重要专业课,旨在介绍编译程序构造的一般原理和基本方法。内容包括语言和文法、词法分析、语法分析、语法制导翻译、中间代码生成、存储管理... -
JS深入理解作用域
2022-02-19 15:32:58三、作用域内部原理 3.1、编译阶段 3.2、执行阶段 3.3、查询阶段 3.4、嵌套阶段 3.5、异常阶段 四、词法作用域 五、遮蔽效应 一、什么是作用域 作用域指一个变量的作用的范围。通常来说,一段程序代码... -
JavaScript作用域原理(二) 预编译[9 29]
2014-09-29 16:00:58在 http://www.cnblogs.com/strick/p/3994209.html 有介绍说明,修正于2014.9.29 -
Javascript中作用域的详细介绍
2020-11-25 16:54:281、编译原理 在传统编译语言的流程中,程序中的一段代码执行前会经历三个步骤。统称为“编译”。 词法分析 将代码字符串分解成有意义的代码块,这些代码块称为词法单元。例如:在js中,var a = 2;。这段程序通常... -
【JS】你不知道的JavaScript 笔记(一)—— 作用域与闭包 - 编译原理 - LHS - RHS - 循环与闭包 - 模块 - ...
2021-07-07 11:40:10之前看了一遍《你不知道的JavaScript(上卷)》之后感觉醍醐灌顶,过了几个月又感觉都快忘了,今天准备边二刷,边做个笔记,把书中的一些重点思想记录整理下来,方便以后复习。...今天先来复习第一部分:作用域与闭包 -
编译原理(龙书,带目录)
2012-04-20 14:42:166.3.3 作用域规则和块结构 232 6.3.4 同层说明的相互作用 236 6.3.5 使用符号表的属性文法的一个 扩充例子 237 6.4 数据类型和类型检查 241 6.4.1 类型表达式和类型构造器 242 6.4.2 类型名、类型说明和递归类型 246... -
前端必经之路:JavaScript底层原理(深入理解JS数据类型、预编译、执行上下文、作用域、构造函数、原型链、...
2019-09-20 20:33:06在预编译过程,如果是在全局作用域下,JavaScript引擎首先会在全局作用域上创建一个全局对象(GO对象,Global Object),并将变量声明和函数声明进行提升。提升后的变量先默认初始化为undefined,而函数则将整个函数... -
编译原理的学习心得和知识总结(一)|初识编译原理
2021-07-02 00:36:551、参考书籍:《编译原理》第三版 清华大学出版社 1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正) 2、本文目的:开源共享 抛砖引玉 一起学习 3... -
华中科技大学编译原理实验四攻略|完整版
2021-12-08 10:05:55助力来年编译原理加大难度!(hhh) MiniC语法分析及中间代码生成 我根据我的实验报告重置了攻略。 贴个完成时间。 文章目录 MiniC语法分析及中间代码生成 实验内容 实验过程 LLVM IR初识 LLVM IR API 语义分析与中间... -
编译原理各章节知识点
2021-04-07 16:34:02编译的阶段划分 两个辅助模块的功能; 前端、后端、遍 编译执行和解释执行 第二章: 词法记号概念及属性 正规式与语言的对应关系 NFA、DFA NFA到DFA的转换 DFA的最简化 直接从语言构造DFA(状态列举法)... -
为什么编译原理被称为龙书?
2020-07-17 08:32:21什么是编译原理 计算机是只认识二进制的,但是我们平常开发中根本不会使用二进制进行开发,我们使用的都是 Java、C 这类的高级语言,每种语言都会经过一系列的转换才能被计算机识别,那么到底是谁做的这项工作呢?... -
JavaScript编译原理
2021-12-20 15:43:02编译原理理解 几个概念:抽象的语法树(AST)、编译语言、分词拆解、JavaScript编译器(Compiler)、解释器(Interpreter)、JavaScript引擎、作用域 步骤: 1、分词,把代码分解为AST代码块 2、解析,由... -
编译原理(一)
2020-05-21 15:47:10什么是编译程序? 编译程序(Compiler): 把某一种高级语言程序等价地转换成另- -种低级语言程序(如汇编语言或机器语言程序)的程序。 解释程序(Interpreter): 把源语言写的源程序作为输入,但不产生目标程序,而是边... -
编译原理详细总结
2020-09-08 16:48:22编译原理 编译概述 把高级程序语言翻译成汇编语言或机器语言的工作称为编译,完成这项翻译工作的软件系统称为编译程序或编译器。下图展示高级语言从编译到执行的大致过程(本文中图片来源均为张莉等编著的《编译... -
【编译原理笔记01】什么是编译,编译系统各结构作用
2019-10-24 15:57:10介绍了什么是编译,并从编译系统的结构出发,对词法分析、语法分析、语义分析、中间代码生成和编译器后段进行概述。 -
编译原理全套
2011-12-03 11:17:216.3.1 无过程嵌套的静态作用域 6.3.2 有过程嵌套的静态作用域 6.3.3 动态作用域 6.4 参数传递 6.4.1值调用 6.4.2 引用调用 6.4.3 复写-恢复调用 6.4.4 换名调用 第7章 中间代码生成 7.1 中间语言 7.1.1 后缀... -
编译原理知识点
2019-05-16 09:15:521. 编译程序(编译器):先将源程序翻译成汇编语言程序或机器语言程序(称为目标程序),然后再执行它。 2. 解释程序(解释器):按解释方式进行翻译的翻译程序称为解释程序。解释程序的主要优点是便于对源程序进行... -
《编译原理及实践》电子书下载
2010-03-25 09:51:066.3.3 作用域规则和块结构 232 6.3.4 同层说明的相互作用 236 6.3.5 使用符号表的属性文法的一个 扩充例子 237 6.4 数据类型和类型检查 241 6.4.1 类型表达式和类型构造器 242 6.4.2 类型名、类型说明和递归类型 246... -
编译原理(十)——语义分析基础
2020-10-05 21:06:06强类型:要求所有用到的表示符必须经过声明,而且在一个程序的作用域内只声明一次,因此需要检查有没有变量的声明、标识符有没有重复声明。 涉及到条件表达式的部分都要求是bool类型或者是逻辑值类型,大多数语言... -
js编译原理学习
2020-12-09 14:54:25一、编译原理 词法分析 将字符串分解成代码块(也叫词法单元)例如:var a = 2;被解析为var、a、=、2 、;。 语法分析 将词法单元流转换成抽象语法树(由元素逐级嵌套所组成的代表了程序语法 结构的树) { ...