一、编译阶段(Compilation Phase):
编译阶段发生在代码执行之前,主要负责将源代码转换成计算机可以直接执行的机器码或字节码。
而这个阶段通常包括以下几个步骤:
1.1、词法分析:
- 将源代码分解成一个个有意义的词汇单元。eg:将
var a = 1;
分解成var
、a
、=
、1
和;
。
1.2、语法分析:
- 根据词法单元构建抽象语法树(AST),AST 是一种树状结构,表示代码的语法结构。eg:
var a = 1;
会被解析成一个赋值表达式的节点。
1.3、代码生成:
- 将优化后的代码转换为机器代码(或者字节码),以便在执行阶段被执行,并且声明变量。eg:
var a = 1;
会声明一个类型为undefined
的全局变量a
。
二、执行阶段(Execution Phase):
执行阶段是代码真正运行的阶段,计算机执行编译阶段生成的机器码或字节码。这个阶段主要包括以下几个步骤:
2.1、加载:
- 将生成的机器码或字节码加载到内存中。
2.2、初始化:
- 初始化程序的运行环境,包括分配内存、设置初始状态等。
2.3、执行:
- 逐行执行代码,进行变量赋值、函数调用等操作。(进行RHS与LHS)
因此我们需要了解两个概念,即LHS查找与RHS查找:
LHS查找:
- LHS 查找是为了找到一个变量的储存位置,以便将一个值赋给它。
var a = 1;
在上述代码中:
var a;
声明了一个变量a
。a = 1;
进行赋值操作,这里就需要进行 LHS 查找,找到a
的存储位置,然后将1
赋值给a
。
RHS查找:
- RHS 查找是为了获取一个变量的值。
var a = 1;
console.log(a);
在上述代码中:
var a = 1;
声明并初始化了一个变量a
。console.log(a);
进行引用操作,这里就需要进行 RHS 查找,找到a
的值,然后将其传递给console.log
函数。
但是RHS与LHS真只有这些性质吗?我们不妨来看串代码。
function foo() {
b = 2;
}
foo();
console.log(b);
在这串代码中,我们发现了一个小问题,这整串代码中 b 从头到尾都未曾被定义类型,那么显然我们会认为运行后必然发生报错,哎?真是这样吗?不妨各位读者自行运行一下,我们就会有个惊人的发现
哎?为什么没有发生报错呢?反而输出了 2 ,这确实是b 的值,这里我们就需要了解一个知识。在运行时,发生了对 b = 2 的LHS查询,而正因如此,我们没有找到b的类型,那么LHS就会默认在全局作用域中创建一个变量 b ,所以在后面运行时,我们能输出b的值。
但是这样的话RHS与LHS的差异是不是太大了。所以,在严格模式下,即代码最上方加入"use strict";
这段代码,将JS变成严格模式,这样再运行上面代码就会发生报错,返还ReferenceError: b is not defined
。
为了方便各位更加清晰的了解RHS与LHS,不妨在下方代码中找找有哪些LHS与RHS吧
function foo(a) {
return a;
}
var c = foo(2);
LHS 引用(共 2 处):
c = foo(2)
: 在var c = foo(2);
这行代码中,我们想要找到c
的容器并将其赋值为foo(2)
的返回值,所以为LHS。a = 2
: 当调用foo(2)
时,会隐式地将参数2
赋给函数的参数a
,所以需要LHS来查找a
的容器。
RHS 引用(共 2 处):
foo(2)
: 在var c = foo(2);
这行代码中,我们需要获取foo()
的值(也就是函数本身)并执行它。 因此是一个 RHS 引用。return a;
: 在return a;
这行代码中,我们需要获取 a 的值,因此是一个 RHS 引用。
小结:
- 编译阶段:将源代码转换成中间代码或机器码,包括词法分析、语法分析、语义分析、代码生成和优化。
- 执行阶段:运行编译阶段生成的代码,包括加载、初始化、执行和运行时环境管理。
- RHS:获取一个变量的值。
- LHS:找到一个变量的储存位置,以便将一个值赋给它。(别忘了它的特殊性质哦~)
对于 JavaScript 这样的解释型语言,编译和执行是交织在一起的,但仍然可以大致分为这两个阶段。理解这两个阶段有助于更好地掌握编程语言的内部机制,从而写出更高效、更可靠的代码。而在分析代码时,区分 LHS 和 RHS 引用可以帮助我们更好地理解代码的执行过程以及作用域的查找规则。
---欢迎各位点赞、收藏、关注,如果觉得有收获或者需要改进的地方,希望评论在下方,不定期更新