本文学习目标:
- 作用域是什么?
- LHS 和 RHS 是什么?
1.1 编译原理
编译语言和解释语言的区别,可以自行了解。
js 常被归为解释语言,但本质是编译语言,只是编译过程非常短暂(通常在执行前几微秒完成)。
js引擎对编译的步骤和传统步骤非常相似,在传统编译语言的流程中,程序源码在执行前经历三个步骤,统称为编译:
补充:
1.分词(Tokenizing)是无状态的机械切割,不关心上下文(比如会把I'm拆成I、'、m);
2.词法分析(Lexing)是有状态的智能解析,会根据上下文合并字符(比如识别I'm是完整的标识符,===是全等运算符而非三个=)。
1.2 作用域的本质:变量查找的“规则手册”
理解作用域,还需要知道引擎和编译器
- 引擎:负责整个 JavaScript 程序的编译与执行,是 “总指挥”;
- 编译器:负责分词、解析、代码生成,在编译阶段处理变量声明;
- 作用域:负责收集所有变量声明,维护变量查询规则,是 “变量管理员”。
自行阅读......
LHS : 引擎问作用域,是否有这个变量,没有向外层作用域询问,一直到最外层作用域,非严格模式下,创造一个全局变量,严格模式下抛出EeferenceError。
RHS: 若在所有作用域中都找不到目标变量,引擎抛出ReferenceError(引用错误)—— 表示 “变量未声明”。
难点解析:
核心原因在于函数声明的编译与执行机制,和普通变量的 “声明→赋值” 流程存在本质差异。
var foo = function(a) {},
编译期:处理var foo 声明;
执行期:处理 foo = function 的赋值,需要LHS 查询。
function foo(a) 编译期: 在当前作用域声明 foo 变量(同普通变量的声明逻辑); 直接将函数体对应的 “函数对象” 绑定到 foo 变量上(而非像普通变量那样留到执行期赋值)。这意味着,编译完成后,foo 已经是一个 “有具体值的函数变量”,而非 “空容器”。