语法解析
作用: 通过对JS的代码块进行基本的语法解析。
如果发现低级的语法错误(
SyntaxError
),就会抛出相应的语法错误(Error),则继续对下一个代码块继续进行语法分析。如果该代码块解析成功,将会进入到下一个阶段预编译阶段。
预编译
完成语法分析的代码块会进入到这个阶段,预编译阶段涉及到执行环境问题。(全局环境和函数环境以及eval环境)
当代码块进入到预编译阶段首先会进入全局环境,
当每个函数函数被调用的时候才会进入函数环境,
预编译四重奏:
- 创建AO对象
- 找形参和变量声明,将变量和形参作为AO属性名,初始属性值为undefiend
- 将实参和形参相统一
- 在函数体中找函数声明,属性值为函数体
最后的程序输出变量值的时候,就是从AO对象中拿。
执行上下文
无论进入什么环境都会创建一个相应的执行上下文, 在此之前,js引擎 会以栈的存取方式来为执行上下文创建一个执行上下文栈(调用栈)。注意,先有执行上下文的栈,再创建执行上下文压栈
- 执行上下文栈的作用: 用来存储和管理创建的执行上下文,相当于一个数组,如果结束执行则销毁该执行上下文。
- 执行上下文栈的特点:栈底永远有且仅有一个全局执行上下文,栈中存在多个执行上下文,栈顶则是当前执行上下文,
全局执行上下文: 一个程序只有一个全局执行上下文。
- 创建一个全局的window对象
- 并设置this 指向window对象
函数执行上下文: 每当一个函数被调用的时候,就会创建一个自己的执行上下文,
执行上下文的属性(分析编译)
- 创建变量对象 (全局环境下创建Global Object, 函数环境中活动的变量对象创建Active Object)
- 检查函数的所有形参并创建属性(若没有实参则为undefiend)
- 检查函数声明并创建属性
- 检查变量声明并创建属性
- 建立作用域链 (用于存储和管理执行上下文的声明的变量和函数)
- 确定this指向 (谁调用指向谁, 全局下指向window对象)
注意: 函数声明会优先于变量声明的提升
执行解析
执行解析阶段:编译结束后,执行阶段负责一段段解析执行。
执行上下文的执行阶段
- 按顺序执行代码 会修改变量值, (变量和形参)
- 形参和实参相统一
其中的作用域以及作用链为代码的执行添加条件:
作用域
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。采用的是词法作用域,也就是静态作用域,所以函数的作用域在函数的定义的时候就决定了。
作用域链
当查找变量的时候, 会先在当前的上下文的变量对象中查找,如果没有,则向上在父级执行上下文中的变量对象中查找,一直找到全局上下文的变量对象, 也就是全局对象, 这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
作用域链的创建过程:
[[scope]] 就是所有父变量对象的层级链。但是不代表完成的作用域链。
如果查找的目的是对变量进行赋值操作,就会使用LHS查询;如果查询的目的是获取变量的值,就会用RHS查询。
Q1:js代码怎么执行?
//js 代码怎么执行的
//js 代码是通过js 引擎执行的
//为什么浏览器能运行js 代码? 为什么运行不了 java
//因为谷歌浏览器中有js 引擎,没有Java引擎
//js 引擎是如何管理并执行 js 代码的? 换言之,js 引擎是如何堆js数据进行增删改查
//1.创建 执行上下文栈 ECstack = []
//2.执行上下文栈中,有执行上下文对象 ECstack = [globalContext,funContext,...]
//3.执行上下文,可以理解为 是对象
//globalContext = {} funContext = {}
//4.执行上下文中有 1.作用域链 Scope 2.变量对象 VO/AO 3.this
//global.[[scope]] = [globalContext.Vo]
//global = {Vo:{},scope:[global.[[scope]]]}
//---> global = {Vo:{},Scope:[globalContext.Vo],this}