前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JS编译原理,LHS与RHS查询,作用域

JS编译原理,LHS与RHS查询,作用域

作者头像
用户10106350
发布2022-10-28 17:58:41
5620
发布2022-10-28 17:58:41
举报
文章被收录于专栏:WflynnWebWflynnWeb

文章来源:https://wflynn.cn/pages/0b0205/ 作者::Miofly

传统编译语言的流程

通常将 JavaScript 归类为“动态”或“解释执行”语言,但事实上它是一门编译语言。

一般会经历三个步骤,统称为“编译”。

分词/词法分析

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

解析/语法分析

这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树。这个树被称为“抽象语法树 ”(Abstract Syntax TreeAST)。var a = 2; 的抽象语法树中可能会有一个叫作 VariableDeclaration 的顶级节点,接下 来是一个叫作 Identifier(它的值是 a)的子节点,以及一个叫作 AssignmentExpression 的子节点。AssignmentExpression 节点有一个叫作 NumericLiteral(它的值是 2)的子节点。

代码生成

AST 转换为可执行代码的过程称被称为代码生成。这个过程与语言、目标平台等息息相关。抛开具体细节,简单来说就是有某种方法可以将 var a = 2; 的 AST 转化为一组机器指令,用来创建一个叫作 a 的变量(包括分配内存等),并将一个值储存在 a 中。

作用域

JavaScript 的编译过程不是发生在构建之前的。 对于 JavaScript 来说,大部分情况下编译发生在代码执行前的几微秒(甚至更短!)的时间内。在我们所要讨论的作用域背后,JavaScript 引擎用尽了各种办法(比如 JIT,可以延迟编译甚至实施重编译)来保证性能最佳。 简单地说,任何 JavaScript 代码片段在执行前都要进行编译(通常就在执行前)。因此, JavaScript 编译器首先会对 var a = 2; 这段程序进行编译,然后做好执行它的准备,并且通常马上就会执行它。

  • 引擎 从头到尾负责整个 JavaScript 程序的编译及执行过程。
  • 编译器 负责语法分析及代码生成等脏活累活
  • 作用域 负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限 用来管理引擎如何在当前作用 域以及嵌套的子作用域中根据标识符名称进行变量查找。。

javascript 对 var a = 2 这段代码的编译运行分析

编译器首先会将这段程序分解成词法单元,然后将词法单元解析成一个树结构,也就是上面提到的分词以及解析。

编译器开始进行代码生成时的处理如下

  • 遇到 var a,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为 a
  • 接下来编译器会为引擎生成运行时所需的代码,这些代码被用来处理 a = 2 这个赋值操作。引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫作 a 的 变量。如果是,引擎就会使用这个变量;如果否,引擎会继续查找该变量如果引擎最终找到了 a 变量,就会将 2 赋值给它。否则引擎就会抛出一个异常!

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

什么是 LHS 与 RHS 查询

最简单的理解可以概括如下:如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。

分析 var a = 2 是什么查询

var a = 2,很显然是将 2 赋值给变量 a,所以这是 LHS 查询

再分析下面几个列子

例一

  • 首先(a) 中的 a 我们要获取 a 这个变量的值,这时就需要执行 RHS 查询
  • 再者 console.log() 本身也需要一个引用才能执行,这时候我们还需要获取 console 的引用,所以要对 console 执行 RHS 查询。当我们查询到 console 的引用后,会检查得到的值中是否有一个叫做 log 的方法 这里不会再对 log 进行 RHS 查询。因为对 console 查询完毕后,对象属性访问规则会接管对 log 属性的访问。对象会接管其下面的属性的访问

例二

代码语言:javascript
复制
function foo(fnn) { 
  console.log(fnn); 
};

foo('girl');
  • 首先 foo(...) 我们需要找到它的引用,这时就需要执行 RHS 查询
  • 然后我们隐式的将 girl 这个值赋值给了函数参数中的 fnn 这个变量,这时就需要执行 LHS 查询
  • 最后对 console.log(fnn) 的分析就和例一一样,对 fnn 进行 RHS 查询,对 console.log 进行 RHS 查询

例三

代码语言:javascript
复制
function foo(fnn) {
  var wfly = fnn;
  return fnn + wfly; 
}
var c = foo('girl');
  • 首先 var c = foo('girl') 中, foo(...) 我们需要找到它的引用,这时就需要执行 RHS 查询,然后将值赋值给 c,执行 LHS 查询
  • 然后我们隐式的将 girl 这个值赋值给了函数参数中的 fnn 这个变量,这时就需要执行 LHS 查询
  • var wfly = fnn; 首先,获取 fnn 变量的值需要执行一次 RHS 查询,接着将 fnn 变量的值赋值给 wfly 变量,执行 LHS 查询
  • 最后 return fnn + wfly 需要对 fnn 执行 RHS 查询,同样的对 wfly 也要执行 RHS 查询

所以共计执行了 4RHS 查询,3LHS 查询

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-02-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WflynnWeb 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 传统编译语言的流程
    • 分词/词法分析
      • 解析/语法分析
        • 代码生成
        • 作用域
          • javascript 对 var a = 2 这段代码的编译运行分析
          • 什么是 LHS 与 RHS 查询
            • 分析 var a = 2 是什么查询
            • 再分析下面几个列子
              • 例一
                • 例二
                  • 例三
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档