JS基础小记 —— 词法作用域 (一)

798 阅读3分钟

一、定义

对于词法作用域,我们可以将其拆分“词法”和“作用域”两部分看。

作用域

何为作用域?在JS代码文本中,一个物理上可见的形式分块(由{ }包裹的代码块,例如try catch块、function X(){...}),JavaScript都会为其创建对应的“块级别”的作用域。

简单来说,代码分块(物理结构上)带来的语法效果是,变量或成员的可见性(即信息隐蔽),而这个可见性的区间(域),被称为作用域。

词法

而这里的词法主要是强调作用域生成的时机,是指JS引擎进行静态代码分析时期,区别于运行期生成的作用域(动态作用域)。

词法环境

词法环境是词法作用域在运行期的实现。代码在物理行显式地进行形式分块,并由引擎执行期映射为词法环境记录,将词法作用域实例化。

值得一提,形式分块在语法分析能分析出(物理可视),而词法作用域是逻辑上、词法分析阶段得出。前者和后者是一对多或一对零的关系。

 

JS的词法作用域可分为表达式、语句、函数、模块和全局级别,举点例子:

① 表达式:JS不存在“表达式级别”的词法作用域,但eval是个例外。eval()是一个“函数调用表达式”,有独立的Eval词法环境;

② 语句:let/const数据声明、一般语句(if{...}else{...})和块语句(try catch、switch);

③ 函数:函数声明function X(){...} ,即函数作用域;

④ 模块:export导出(该作用域会包含所有export的名字,由外部import);

⑤ 全局:由宿主程序实现。

 

小结:词法作用域 = 静态词法分析(状态/时机)得出的作用域(产物)

 

二、词法作用域之间的相互关系

在JS中,作用域是互不相交的,也就是作用域之间只存在平行或嵌套两种相关性。具体规则为:

1. 相同级别的词法作用域可以相互嵌套、穿透;

2. 高级别的词法作用域可以包含低级别的词法作用域;

3. 低级别词法作用域不能包含高级别的词法作用域。因此其可以理解成低级别和高级别词法作用域的平行关系或者语法上的违例。

  

规则一

第一条规则应用很常见。例如以下例子,if和while处于语句级别的词法作用域(级别2),因此if语句包含了while语句的词法作用域:

image.png

而同级别的词法作用域的穿透,如以下例子:

image.png

break标签子句是可以穿过同级别的switch语句的词法作用域(同为级别2)。

规则二

第二条规则也是很常见的,例如以下写法,级别3的函数词法作用域包含级别2的if语句词法作用域:

image.png

规则三

那第三条规则怎么理解呢?还是以例子说明,下面的示例1和示例2是完全等效的。语句无法包含比它更高级别的函数词法作用域,因此示例1的嵌套关系可以理解成平行关系。

image.png image.png

小结:词法作用域之间只存在嵌套或平行关系。JS没有GOTO语句,因此词法作用域不存在相交,即执行流程是不能从一个作用域直接跳入到另一个同等级的作用域,只能是跳出当前作用域。