摘要:关于两个专业术语的讨论起自对你不知道的一书的阅读学习。遇到,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。摘录来自你不知道的。
JS 编译之 LHS RHS 一、前言
最近和朋友聊技术的时候,聊到 LHS RHS,我竟然没听说过 没听说过。。。 于是成功引起了我的好奇心。 关于两个专业术语的讨论起自对《你不知道的JavaScript》一书的阅读学习。
二、编译简述尽管通常将JavaScript归类为“动态”或“解释执行”语言,但事实上它是一门编译语言。这个事实对你来说可能显而易见,也可能你闻所未闻,取决于你接触过多少编程语言,具有多少经验。但与传统的编译语言不同,它不是提前编译的,编译结果也不能在分布式系统中进行移植。
尽管如此,JavaScript引擎进行编译的步骤和传统的编译语言非常相似,在某些环节可能比预想的要复杂。
在传统编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为“编译”。
分词/词法分析(Tokenizing/Lexing)
解析/语法分析(Parsing)
代码生成
比起那些编译过程只有三个步骤的语言的编译器,JavaScript引擎要复杂得多。例如,在语法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。
JavaScript引擎,编译器概述
从头到尾负责整个JavaScript程序的编译及执行过程。
引擎的好朋友之一,负责语法分析及代码生成等脏活累活。
摘录来自: “你不知道的JavaScript(上卷)”
以上内容看起来有些高深,但是与下面内容并无多少关联,只是背景,有兴趣的可以深入研究一下。
对于下面的内容,我们只需要知道 JS代码的执行需要JS引擎的,而JS引擎的在执行代码前会先对其进行编译(有些地方称之为预解析),引擎最终执行的是经过编译之后的代码。
LHS RHS 这两个术语就是出现在引擎对变量进行查询的时候,接下来看看在具体例子中 站在编译器和引擎的角度看看它们是怎么思考工作的,以及这个过程中如何触发了LHS RHS.
以书中的赋值语句为示例
var a = 2;
事实上编译器会进行如下处理。
遇到var a,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是,编译器会忽略该声明,继续进行编译;否则它会要求作用域在当前作用域的集合中声明一个新的变量,并命名为a。
接下来编译器会为引擎生成运行时所需的代码,这些代码被用来处理a = 2这个赋值操作。引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫作a的变量。如果是,引擎就会使用这个变量;如果不是,引擎会继续查找该变量。
摘录来自: 你不知道的JavaScript。
以上为书中描述,但是个人觉得可以补充一些描述如下
编译器在编译一段js代码时,相当于对这段代码进行预解析,为了引擎更好的进行二次解析。在预解析时,先构建好代码的上下文环境,建立作用域链,在每个作用域中进行变量声明提升和更特别的函数声明提升。变量的声明提升会在当前作用域的集合中声明一个新的变量,如a。另外,在声明每一个新的变量之前,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中, 如有则报错。
如
console.log(a) var a = 2;
的预解析之后,其实相当于
var a console.log(a) // undefined a = 2;
接下来编译器会为引擎生成运行时所需的代码,这些代码被用来处理a = 2这个赋值操作。引擎运行时会首先询问作用域,在当前的作用域集合中是否存在一个叫作a的变量。如果是,引擎就会使用这个变量;如果不是,引擎会沿作用域链继续查找该变量;如果查到根作用域也没有查到,会自动声明a。
ps: 关于声明提升,作用域链,如有疑惑,后续章节解释。
上述过程中,第二步中编译器生成了代码,引擎执行它时(第3行 a = 2),会通过查找变量a来判断它是否已声明过。查找的过程由作用域进行协助,但是引擎执行怎样的查找,会影响最终的查找结果。
此时引擎会就在对变量a进行LHS查询,另外一个查询的类型叫RHS。
我打赌你一定能猜到“L”和“R”的含义,它们分别代表左侧和右侧。
什么东西的左侧和右侧?是一个赋值操作的左侧和右侧。换句话说,当变量出现在赋值操作的左侧时进行LHS查询,出现在右侧时进行RHS查询。
讲得更准确一点,RHS查询与简单地查找某个变量的值别无二致,而LHS查询则是试图找到变量的容器本身,从而可以对其赋值。从这个角度说,RHS并不是真正意义上的“赋值操作的右侧”,更准确地说是“非左侧”。
你可以将RHS理解成retrieve his source value(取到它的源值),这意味着“得到某某的值”。
LHS和RHS的含义是“赋值操作的左侧或右侧”并不一定意味着就是“=赋值操作符的左侧或右侧”。赋值操作还有其他几种形式,因此在概念上最好将其理解为“赋值操作的目标是谁(LHS)”以及“谁是赋值操作的源头(RHS)”。
摘录来自: 你不知道的JavaScript。
以上为书中解释,以赋值操作符为标志,加上一些特殊情况的理解。但是我觉得可以有其他的理解方式。
五、LHS RHS 个人理解讲得更准确一点,RHS查询与简单地查找某个变量的值别无二致,而LHS查询则是试图找到变量的容器本身,从而可以对其赋值。
举个例子来理解就是:RHS是找到你在哪个座位,看看你的样子拍个照;LHS同样是找到你在哪个座位,但是一眼不看直接踹飞 换个人坐这儿。
简单来讲有两个区别:1.关不关心你现在的状态 2.是否改变你的状态(不管是改变一部分,还是完全改变)。
所以书中这句原话
是引擎执行怎样的查找,会影响最终的查找结果。
我觉得可以反过来理解,正是因为查找结果的不同分出了两种查找类型。
左侧右侧的分类理解更多是以 代码的形态为标准分类的。个人觉得可以按查找目的为标准分类,RHS是为了读取变量的值,LHS是为了改变变量的值。
六、总结LHS RHS就是对变量查询的两种查询类型,区别在于查询的结果,或者说查询的目的,在代码上直观体现为变量位置形态的不同。其中涉及的编译器引擎工作过程,对于更深入的理解掌握js很有帮助。 其中个人理解有不合适的地方还请指正。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/96096.html
摘要:查询是在作用域链中,一级级的往上查找该变量的引用。作用域和作用域链作用域的概念,应该两张图几句话就能解释吧。这个建筑代表程序中的嵌套作用域链。一层嵌一层的作用域形成了作用域链,变量在作用域链中的函数内得到了自己的定义。 javascript作用域和闭包之我见 看了《你不知道的JavaScript(上卷)》的第一部分——作用域和闭包,感受颇深,遂写一篇读书笔记加深印象。路过的大牛欢迎指点...
摘要:比如程序会被分解为解析语法分析将词法单元流转换成一个由元素逐级嵌套所组成的代表了程序语法接口的书,又称抽象语法树。代码生成将抽象语法树转换为机器能够识别的指令。 showImg(https://segmentfault.com/img/remote/1460000009682106?w=640&h=280); 本文首发在我的个人博客:http://muyunyun.cn/ 《你不知道的...
摘要:的抽象语法树中可能如下图所示代码生成将转换为可执行代码的过程被称为代码生成。如果是,编译器会忽略该声明,继续进行编译,否则它会要求在当前作用域的集合中声明一个新的变量,并命名为。 在学习 javascript 的过程中,我们第一步最应该了解和掌握的就是作用域,与之相关还有程序是怎么编译的,变量是怎么查找的,js 引擎是什么,引擎和作用域的关系又是什么,这些是 javascript 这门...
摘要:如果查找的目的是对变量进行赋值,就会使用查询如果目的是获取变量的值,就会用查询。赋值操作会导致查询。接下来,会查询查询变量并对其进行赋值。不成功的引用会导致抛出异常。 简述编译原理 JavaScript 程序中的一段源代码在执行之前会经历三个步骤,统称为 编译 分词/词法分析 解析/语法分析 代码生成 先看原书对一个赋值操作的拆解说明: 变量的赋值操作会执行两个动作,首先编译器会...
摘要:的抽象语法树中可能会有一个叫作的顶级节点,接下来是一个叫作它的值是的子节点,以及一个叫作的子节点。值得注意的是,是非常重要的异常类型。严格模式下,未声明的和俩者行为相同,都会是。 你不知道的JS(上卷)笔记 你不知道的 JavaScript JavaScript 既是一门充满吸引力、简单易用的语言,又是一门具有许多复杂微妙技术的语言,即使是经验丰富的 JavaScript 开发者,如果...
阅读 2342·2021-11-23 09:51
阅读 1997·2021-10-14 09:43
阅读 2759·2021-09-27 13:35
阅读 1143·2021-09-22 15:54
阅读 2494·2021-09-13 10:36
阅读 3781·2019-08-30 15:56
阅读 3404·2019-08-30 14:09
阅读 1709·2019-08-30 12:57