Javascript编译原理简介

一. 传统编译语言编译原理

这一节主要简单了解下①传统编译语言的编译流程/Javascript涉及的角色和流程,与变量声明并赋值时所涉及到的②LHS查询③RHS查询

Javascript引擎进行编译的步骤和传统的编译语言非常相似,在某些环节可能比预想的要复杂。在传统的编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为“编译”(下面介绍的是传统编译语言的流程)。

①分词/词法分析(Tokenizing/Lexing)

这个过程会将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代码块被称为词法单元(token)。例如,考虑程序var a = 2;。这段程序通常会被分解成为下面这些词法单元:var、a、=、2、;。空格是否会被当作词法单元,取决于空格在这门语言中是否具有意义。

提示:分词(tokenizing)和词法分析(Lexing)之间的区别是非常微妙、晦涩的,主要差异在于词法单元的识别是通过有状态还是无状态的方式进行的。简单来说,如果词法单元生成器在判断a是一个独立的词法单元还是其它词法单元的一部分时,调用的是有状态的解析规则,那么这个过程就被称为词法分析。

②解析/语法分析(Parsing)

这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树。这个树被称为“抽象语法树”(Abstract Syntax Tree,AST)。

例如:var a = 2;的抽象语法树中可能会有一个叫做VariableDeclaration(var)的顶级节点,接下来是一个叫做Identifier(a)的子节点,以及一个叫做AssignmentExpression(=)的子节点。AssignmentExpression节点由一个叫做NumericLiteral(它的值是2)的子节点。

③代码生成

将AST转换为可执行代码的过程被称为代码生成。这个过程与语言、目标平台等息息相关。

抛开具体细节,简单来说就是有某种方法可以将var a = 2;的AST(Abstract Syntax Tree)转换成一组机器指令,用来创建一个叫做a的变量(包括内存分配等),并将一个值存储在a中。

比起那些编译过程中只有三个步骤的语言的编译器,Javascript引擎要复杂的多。例如,在语法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。 

二. 理解作用域

2.1 演员表

首先介绍将要参与到对程序var a = 2; 进行处理的过程中的演员们。

①引擎

从头到尾负责整个Javascript程序的编译及执行过程

②编译器

引擎的好朋友之一,负责分词/词法分析(将代码分解成有意义的代码块)、语法分析(将代码生成抽象语法树Abstract Syntax Tree)及代码生成(将AST转成可执行代码的过程)

③作用域

引擎的另外一位好朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。

对话

var a = 2;这段程序,引擎认为这里有两个完全不同的声明,一个由编译器在编译时处理,另一个则由引擎在运行时处理。

下面我们将var a = 2;分解,看看引擎和它的朋友们是如何协同工作的。

编译器首先会将这段程序分解成词法单元,然后将词法单元解析成一个抽象语法树(AST)。但是当编译器开始进行代码生成时,它对这段代码的处理方式会和预期有所不同。

可以合理的假设编译器所产生的代码能够用下面的伪代码进行概括:“为一个变量分配内存,将其命名为a,然后将值保存进这个变量”。然而, 这并不完全正确。

事实上编译器会进行如下处理:

1. 遇到var a,编译器会询问作用域中是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为a。

2. 接下来编译器会为引擎生成运行时所需的代码,这些代码被用来处理a = 2这个赋值操作。引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫做a的变量。如果是,引擎就会使用这个变量;如果否,引擎会继续查找该变量。如果引擎最终找到了a变量,就会将2赋值给它,就会将2赋值给它。否则引擎就会举手示意并抛出一个异常!

总结:变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对其进行赋值。

2.2 进一步了解

编译器在编译过程的第二步生成了代码,引擎执行它时,会通过查找变量a来判断它是否已声明过。查找的过程由作用域进行协助。

在我们的例子中(var a = 2),引擎会为变量a进行LHS查询。另一个查找的类型叫做RHS。

LHS:当变量出现在赋值操作的左侧时进行LHS查询。

RHS:当变量出现在右侧进行RHS查询,更准确一点,RHS查询与简单地查找某个变量的值别无二致,而LHS查询则是找到容器的本身,从而可以对其进行赋值。从这个角度说,RHS并不是真正意义上的“赋值操作的右侧”,更准确地说是“非左侧”。可以将RHS理解成retrieve his source value(取到它的源值),这意味着“得到某某的值”。

如:console.log(a);,这里对a的引用是一个RHS引用,因为这里a并没有赋予任何值。相应地,需要查找并取出a的值,再将值传递给console.log(...)。

直接理解成:“赋值操作的目标是谁(LHS)”以及“谁是赋值操作的源头(RHS)”。

再看一个例子

function foo(a) {

        console.log(a);

}

foo(2);

最后一行foo(...)函数的调用需要对foo函数本身进行RHS引用,意味着去找到foo的值,并把它给我。然后执行console.log(a)之前还有一个隐式操作a=2,2会被分配给参数a。为了给参数a(隐式地)分配值,需要进行一个LHS查询。接着执行console.log(...)时,会先执行RHS查询到console对象,并且检查得到的值中是否有一个叫做log的方法。最后,在概念上可以理解成在LHS和RHS之间通过值2进行交互来将其传递进log(...)(通过变量a的RHS查询)。假设在log(...)(通过变量a的RHS查询)。假设在log(...)函数的原生实现中它可以接受参数,在将2值给其中第一个(也许叫做arg1)参数之前,这个参数需要进行LHS引用查询。

注意:你可能会倾向于将函数声明function foo(a) {...概念化为普通的变量声明和赋值,比如var foo、foo = function {...。如果这样理解的话,这个函数声明将需要进行LHS查询。

然而还有一个重要的细微差别,编译器可以在代码生成的同时处理声明和值的定义,比如引擎在处理代码时,并不会有线程专门用来将一个函数值“分配给”foo。因此,给函数声明理解成前面讨论的LHS查询和赋值的形式并不合适。

最后一个例子:

function foo(a) {

        var b = a;

        return a + b;

}

var c = foo(2);

1、找出所有的LHS查询:

c = ..; a = 2(隐式变量分配)、b = ..

2、找出所有的RHS查询

foo(2..、= a;、a..、..b

LHS 和 RHS 查询都会在当前执行作用域中开始,如果有需要(当前没找到),就会向上级作用域继续查找目标标识符(作用域链)。
不成功的 RHS 会导致抛出 ReferenceError 异常。不成功的 LHS 会自动隐式在全局作用域中创建一个全局变量(非严格模式下),该变量使用 LHS 引用的目标作为标识符。(如果是严格模式下也会抛出 ReferenceError 异常)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/745338.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

acados_TypeError: __init__() got an unexpected keyword argument ‘winmode‘

TypeError: init() got an unexpected keyword argument ‘winmode’ 1. TypeError: init() got an unexpected keyword argument ‘winmode’ python vehicle_move_ocp.py执行完成以后,Traceback (most recent call last): File “vehicle_move_ocp.py”, line 172, in ac…

在虚拟环境中配置0.4.1版本的pytorch

家人们,使用下面方法安装pytorch的时候一定要记得关掉外网!!! 我下午浪费了几个小时安不上,就是因为没有关掉外网! QAQ 安装方法链接

发布组件到npm

1.环境准备&#xff0c;需要装好node&#xff0c;注册号npm账号,这里不做详解 2.创建编写组件和方法的文件夹package 3.在文件夹中创建需要定义的组件&#xff0c;并且加上name属性 //组件 <template><div><button>按钮组件</button></div> &…

VSSM VMamba实现

文章目录 VSSM维度变换初始化模型参数初始化模型搭建def_make_layerdef _make_downsample patch embed第一至四阶段分类器 VSSBlockdef __ init__ssm分支mlp分支 def forward VSSM Mamba实现可以参照之前的 mamba_minimal系列 论文地址&#xff1a; VMamba 论文阅读&#xff1…

css3常见选择器

使用工具 Visual Studio Code 1.CSS3基础选择器 1.1 标签选择器 1.2.1 标签选择器的语法 一个完整的HTML5页面是由很多不同的标签组成的&#xff0c;而标签选择器则决定标签应采用的CSS样式&#xff0c;语法如下:标签名{ 属性1&#xff1a;属性值1&#xff1b; 属性2&…

Vscode 修改C++版本

1. 首先要检查GCC版本&#xff0c;有的gcc版本过低会导致C版本升级不成功 可以用cmd&#xff0c;用gcc --version命令查看gcc版本 我这里就是gcc版本较低&#xff0c;不支持c17 需要先升级gcc版本 gcc与c对应的版本&#xff0c;大家可以在这位大佬的博客中看&#xff0c;写…

python 读取pdf 将每页转成jpg

需要安装fitz pip install PyMuPDF 这里我发现了问题,默认安装最新版本1.21.x 但是不支持大部分网上的api 所以分开两部分 1.21.x的 import fitz # PyMuPDF from PIL import Imagedef extract_images_from_tiff(tiff_path, output_folder):# 打开 TIFF 文件pdf_document f…

经典排序算法之计数排序|c++代码实现

引言 排序算法c实现系列第8弹——计数排序。 计数排序是理解起来相对简单的一个排序算法&#xff0c; 计数排序 计数排序&#xff08;Counting Sort&#xff09;是一种非比较型的排序算法&#xff0c;它的基本思想是统计待排序数组中每个元素的出现次数&#xff0c;然后根据…

基于grafana+elk等开源组件的 云服务监控大屏架构

本套大屏,在某云服务大规模测试环境,良好运行3年. 本文主要展示这套监控大屏的逻辑架构.不做具体操作与配置的解释. 监控主要分为三部分: 数据展示部分数据存储数据采集 1. 数据展示 数据展示方面主要使用grafana 2. 数据存储 根据数据种类和特性和用途的不同,本套监控采…

Intelli idea 自带maven路径和配置

自带maven位于&#xff1a;plugins/maven/lib/maven3 Mac配置maven环境变量&#xff1a; #maven export MAVEN_HOME/maven根路径 export PATH$MAVEN_HOME/bin:$PATH#刷新环境变量 source ~/.bash_profile#查看maven版本 mvn -version#查看依赖树 mvn dependency:tree 配置ma…

django-q轻量级定时任务制定

django-q ,celery&#xff0c;apschedule都可以作为python的选型&#xff0c;但是django-q更轻量级&#xff0c;可以定制想要的任务&#xff0c;通过消息中间件&#xff0c;来实现不太高并发的实现 官网介绍地址 django-q官网地址 本次测试的是python3.12版本 首先需要安装dja…

几何相互作用GNN预测3D-PLA

预测PLA是药物发现中的核心问题。最近的进展显示了将ML应用于PLA预测的巨大潜力。然而,它们大多忽略了复合物的3D结构和蛋白质与配体之间的物理相互作用,而这对于理解结合机制至关重要。作者提出了一种结合3D结构和物理相互作用的几何相互作用图神经网络GIGN,用于预测蛋白质…

架构实战--以海量存储系统讲解热门话题:分布式概念

关注我&#xff0c;持续分享逻辑思维&管理思维&#xff1b; 可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导&#xff1b; 有意找工作的同学&#xff0c;请参考博主的原创&#xff1a;《面试官心得--面试前应该如何准备》&#xff0c;《面试官心得--面试时如何进行自…

Nodejs 第五十七章(addon)

Nodejs在IO方面拥有极强的能力&#xff0c;但是对CPU密集型任务&#xff0c;会有不足&#xff0c;为了填补这方面的缺点&#xff0c;Nodejs支持c/c为其编写原生nodejs插件&#xff0c;补充这方面的能力。 Nodejs c扩展 c编写的代码能够被编译成一个动态链接库(dll),可以被nod…

VMware workstation的安装

VMware workstation安装&#xff1a; 1.双击VMware-workstation-full-9.0.0-812388.exe 2.点击next进行安装 选择安装方式 Typical&#xff1a;典型安装 Custom&#xff1a;自定义安装 选择程序安装位置 点击change选择程序安装位置&#xff0c;然后点击next 选择是否自动…

vue 如何实现手机横屏功能

功能背景 有些需求需要手动实现横屏功能&#xff0c;比如点击一个横屏的图标将整个页面90度翻转&#xff0c;再点击退出横屏回到正常页面。 实现思路 一拿到这个需求很可能就被吓到了&#xff0c;但简单来说&#xff0c;就是点击横屏按钮跳转一个新页面&#xff0c;通过 cs…

MySQL模块---安装并配置

1. 在项目中操作数据库的步骤 ① 安装操作 MySQL 数据库的第三方模块&#xff08;mysql&#xff09; ② 通过 mysql 模块链接到 MySQL 数据库 ③ 通过 mysql 模块执行 SQL 语句 2. 安装 mysql 模块 这里要安装的是 mysql2 也就是 mysql 8.0后面的版本 npm init -y npm…

手动创建线程池各个参数的意义?

今天我们学习线程池各个参数的含义&#xff0c;并重点掌握线程池中线程是在什么时机被创建和销毁的。 线程池的参数 首先&#xff0c;我们来看下线程池中各个参数的含义&#xff0c;如表所示线程池主要有 6 个参数&#xff0c;其中第 3 个参数由 keepAliveTime 时间单位组成。…

【Linux】linuxCNC+Qt+Opencascade+kdl+hal 实时6轴机器人控制器

CNC机器人 程序框架 机器人模型 笔记&#xff1a; debian重启后 无法打开共享目录 最新版搜狗输入法安装后不支持中文&#xff0c;需要安装旧版本的 sogoupinyin_4.0.1.2800_x86_64.deb可用 数控机器人在哪些领域应用有优势 数控机器人在多个领域都展现出了显著的优势&#xff…

介绍一下redis中底层磁盘及IO模型,数据持久化机制,哨兵机制

底层磁盘及IO模型&#xff1a; Redis中的数据存储在内存中&#xff0c;但为了保证数据的持久化&#xff0c;Redis还提供了两种数据持久化方式&#xff1a;RDB&#xff08;Redis DataBase&#xff09;和AOF&#xff08;Append-Only File&#xff09;。 RDB&#xff1a;RDB是一种…