简述
对于常见编译型语言(例如: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是怎么被绑定的?
在创建可执行上下文的时候,根据代码的执行条件,来判断分别进行默认绑定、隐式绑定、显示绑定等。
作用域链是怎么形成的?
可执行上下文中的词法环境中含有外部词法环境的引用,我们可以通过这个引用获取外部词法环境的变量、声明等,这些引用串联起来一直指向全局的词法环境,因此形成了作用域链。
闭包是怎么形成的?
可执行上下文中的词法环境中含有外部词法环境的引用,我们可以通过这个引用获取外部词法环境的变量、声明等,因此形成了闭包。

京公网安备 11010502036488号