简述
对于常见编译型语言(例如:Java)来说,编译步骤分为:词法分析->语法分析->语义检查->代码优化和字节生成。
对于解释型语言(例如JavaScript)来说,通过词法分析和语法分析得到语法树后,就可以开始解释执行了。
编译阶段
词法分析
词法分析是将字符流(char stream)转换为记号流(token stream),就像英文句子一个个单词独立翻译,举例:
代码:var result = testNum1 - testNum2;
词法分析后 :
NAME "result" EQUALS NAME "testNum1" MINUS NAME "testNum2" SEMICOLON
语法分析
语法分析得到抽象语法树
{ "type": "Program", "start": 0, "end": 34, "body": [ { "type": "VariableDeclaration", "start": 0, "end": 34, "declarations": [ { "type": "VariableDeclarator", "start": 4, "end": 34, "id": { "type": "Identifier", "start": 4, "end": 10, "name": "result" }, "init": { "type": "BinaryExpression", "start": 15, "end": 34, "left": { "type": "Identifier", "start": 15, "end": 23, "name": "testNum1" }, "operator": "-", "right": { "type": "Identifier", "start": 26, "end": 34, "name": "testNum2" } } } ], "kind": "var" } ], "sourceType": "module" }
当JavaScript解释器在构造语法树的时候,如果发现无法构造,就会报语法错误,并结束整个代码块的解析。
预编译
当JavaScript引擎解析脚本时,它会在预编译期对所有声明的变量和函数进行处理!并且是先预声明变量,再预定义函数。如果存在变量提升,则先提升变量再提升函数,但是字面量定义的函数不会被提升,即是变量提升会被函数提升覆盖,但是同名的函数与变量会被后面的变量赋值所覆盖
执行阶段
JavaScript有两个重要的组成部分,一个是内存堆,一个是执行栈
内存堆--内存分配的地方
如下代码,变量name
和函数hello
就是被存放在内存堆中
var name = 'David' function hello(name){ console.log(`hello,${name}!`) }
执行栈--代码执行的地方
当我们调用这个函数时,即hello()
,这个时候它就会被压进执行栈中
执行上下文
执行上下文在代码块执行前创建,主要有三种
- 全局执行上下文 — 这是基础上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局的 window 对象(浏览器的情况下),并且设置 this 的值等于这个全局对象。一个程序中只会有一个全局执行上下文。
- 函数执行上下文 — 每当一个函数被调用时, 都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数上下文可以有任意多个。
- Eval 执行上下文 — 执行在 eval 内部的代码也会有它属于自己的执行上下文,不推荐使用。
执行上下文分为两个阶段
- 创建阶段
- 执行阶段
创建阶段
执行上下文的创建阶段主要解决以下三点:
- 决定 this 的指向
- 创建词法环境(LexicalEnvironment)
- 创建变量环境(VariableEnvironment)
this指向
我们应该知道this的指向是在代码执行阶段确定的,所谓的『代码执行阶段』正是『执行上下文的创建阶段』。默认情况下this指向全局对象,比如浏览器中的window.此外可能存在隐式绑定的情况。
词法环境
词法环境分为三大类:
- 全局环境:全局环境的外部环境引用是 null,它拥有内建的 Object/Array/等、在环境记录器内的原型函数(关联全局对象,比如 window 对象)还有任何用户定义的全局变量,并且 this的值指向全局对象。
- 模块环境:包含模块顶级声明的绑定以及模块显式导入的绑定。 模块环境的外部环境是全局环境。
- 函数环境:函数内部用户定义的变量存储在环境记录器中,外部引用既可以是其它函数的内部词法环境,也可以是全局词法环境
词法环境本身包括两个部分:
- 『环境记录器(Environment Record)』是存储变量和函数声明的实际位置
- 『外部环境的引用(outer Lexical Environment)』指它可以访问其父级词法环境(即作用域)
变量环境
变量环境也是一个词法环境,但不同的是词法环境被用来存储函数声明和变量(let 和 const)绑定,而变量环境只用来存储 var 变量绑定。
阐述几个问题
this是怎么被绑定的?
在创建可执行上下文的时候,根据代码的执行条件,来判断分别进行默认绑定、隐式绑定、显示绑定等。
作用域链是怎么形成的?
可执行上下文中的词法环境中含有外部词法环境的引用,我们可以通过这个引用获取外部词法环境的变量、声明等,这些引用串联起来一直指向全局的词法环境,因此形成了作用域链。
闭包是怎么形成的?
可执行上下文中的词法环境中含有外部词法环境的引用,我们可以通过这个引用获取外部词法环境的变量、声明等,因此形成了闭包。