编译
在传统编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为“编译”。
编译可以分为三个阶段
- 词法分析。例如,
var a = 2;
这段代码通常会被分解成为下面这些词法单元:var
、a
、=
、2
、;
- 语法分析。这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树。这个树被称为“抽象语法树”(Abstract Syntax Tree,AST)在线生成 AST
- 代码生成。将 AST 转换为可执行代码的过程被称为代码生成。简单来说就是有某种方法可以将
var a = 2;
的 AST 转化为一组机器指令,用来创建一个叫作 a 的变量(包括分配内存等),并将一个值储存在 a 中。
词法阶段
词法作用域
词法作用域就是定义在词法阶段的作用域。
换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变(大部分情况下是这样的)。
function foo(a) {
var b = a * 2;
function bar(c) {
console.log( a, b, c );
}
bar( b * 3 );
}
foo( 2 ); // 2, 4, 12
代码中有三个作用域。
- 全局作用域,只有一个 foo 标识符
- foo 创建的作用域,三个标识符,a b c
- bar 创建的作用域,一个标识符,c
这三个作用域由其对应的作用域块代码写在哪里决定,它们是逐级包含的。而且是严格包含,没有哪个作用域同时出现在两个作用域里。
查找范围
作用域查找会在找到第一个匹配的标识符时停止。在多层的嵌套作用域中可以定义同名的标识符,这叫作 “遮蔽效应”(内部的标识符“遮蔽” 了外部的标识符)。抛开遮蔽效应,作用域查找始终从运行时所处的最内部作用域开始,逐级向外或者说向上进行,直到遇见第一个匹配的标识符为止。
无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定。
词法作用域查找只会查找一级标识符,比如 a、b 和 c。如果代码中引用了 foo.bar.baz
这种,词法作用域查找只会试图查找 foo
标识符,找到这个变量后,** 对象属性访问规则 ** 会分别接管对 bar 和 baz 属性的访问。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论。