《用得上的前端知识》系列 - 你我都很忙,能用100字说清楚,绝不写万字长文
基本概念
执行上下文:可以理解为 JavaScript 执行一段代码时的运行环境,比如调用一个函数,就会进入这个函数的执行上下文,确定该函数在执行期间用到的诸如 this、变量、对象以及函数等。
代码的执行与上下文的创建
JS 代码的执行过程:
- 编译阶段,通过“词法分析”和“语法分析”构建出源码对应的抽象语法树(AST);
-
- 词法分析,把源代码作为一段字符串,分解成若干有意义的代码字符(也叫“词法单元”,token),随后生成一个词法单元对象集合 tokens,每个词法单元对象都包含了该 token 的类型(type)和值(value);
- 语法分析,根据 tokens 中各 token 之间的关系形成反映整个程序语法的树状结构,也就是语法树 AST。
- 预处理阶段
-
- 根据已构建的 AST 创建 EC(执行上下文),同时进行标识符的绑定与初始化;
- 根据 AST 生成字节码。
- 执行阶段。
执行上下文的创建
- 通过“词法分析”和“语法分析”构建 抽象语法树(AST);
- 根据 AST 创建 执行上下文;
- 创建一个新的 词法环境对象;
- 将该执行上下文的 变量环境组件(VariableEnvironment) 和 词法环境组件(LexicalEnvironment) 都指向新创建的 词法环境对象;
- 将创建的执行上下文 推入执行上下文栈 并成为 正在运行的 执行上下文;
- 对代码块内的标识符进行绑定及初始化;
- 运行代码;
- 运行完毕后 执行上下文 出栈;
- 继续执行 执行上下文栈 栈顶的上下文对应的代码,直至所有 执行上下文 对应的代码被执行完毕。
注意:只有当整个应用程序结束的时候,调用栈才会被清空,所以程序结束之前, 调用栈最底部永远有个全局执行上下文。
标识符绑定及初始化
- 将码块内的let、const和class声明的标识符合集记录为lexNames;
- 将代码块内的var和function声明的标识符合集记录为varNames;
- 如果lexNames内的任何标识符在varNames或lexNames内出现过,则报错SyntaxError;
- 将varNames内的var声明的标识符进行绑定并初始化为 undefined,如果有同名标识符则跳过;
- 将lexNames内的标识符进行绑定,但其值并不会进行初始化,在运行至其声明处代码时才会进行初始化,在初始化前访问都会报错(暂时性死区);
- 最后将varNames内的函数声明进行标识符绑定并初始化赋值对应的函数体。
-
- 如果有同名函数声明,则前面的都会忽略,只有最后一个声明的函数会被初始化赋值;
- 如果变量与函数同名,则函数声明将覆盖变量声明。
举例说明
let a = '面包';
var b = '可乐';
// (1)
if( true ){
const c = '汉堡'; // (2)
}
function helloFn(){
var d = '鸡翅'; // (3)
}
fn();
随着代码的编译、执行,执行上下文 中标识符的绑定和赋值情况如图中黄色卡片的标注:
参考资料
- github.com/logan70/Blo…
- www.w3.org/html/ig/zh/… – ES5 中文文档
- 262.ecma-international.org/5.1/ – ES5 英文文档
- 262.ecma-international.org/6.0/#sec-le… – ES6 英文文档
- www.qiwenke.com/2017/03/24/…