函数执行流程
定义阶段:
确定函数的作用域[[scope]]-> 就是把函数的所有父变量都放进去
执行阶段:
- 创建函数的执行上下文,并将其压入上下文执行栈中。开始初始化工作
- 复制 [[scope]] 到作用域链
- 使用 Arguments 函数创建活动对象 (AO)
- 将 AO 压入作用域链
- 开始执行函数
- 函数执行完毕,将函数执行上下文弹出
执行上下文
1. 执行上下文栈
在JavaScript引擎执行脚本的时候,会使用执行上下文栈的东西管理函数执行的顺序极其作用域等。
在这个栈中,每一个栈帧都是一个执行上下文,最底层的是全局上下文。
2.执行上下文
执行上下文可以简单的理解为JS代码的运行环境,每一段JS代码在执行时都是在对应的上下文中进行。
上下文的分类
一共可以分成3类:
-
全局执行上下文
这个上下文有且只能有一个,在上下文栈中处于最底层,在脚本执行开始时被加载
-
函数执行上下文
在函数执行时创建,执行完毕时销毁。可以有多个
-
eval执行上下文
不常用,不在此赘述
生命周期
创建(初始化)-> 执行 -> 回收
上下文的重要组成部分*(重要!!!)*
- this指针
- 变量对象
- 作用域链
全局上下文
全局上下文是执行栈中最底层的执行环境,可以理解为最外围的执行环境。这个上下文有且只有一个。在浏览器中,一般是 window对象。
函数上下文
在函数上下文中,一般用活动对象来表示变量对象。因为JS环境是单线程的,因此同时只能有一个上下文在执行,而这个上下文叫做活动对象。只有活动对象才能被访问到,其余的变量对象都不能被访问。
变量对象
在创建上下文时,变量对象也会被同时创建,是上下文中的数据区域,用来存储上下文中声明的变量和函数声明。
与上下文一样,变量对象也分为两种:全局变量对象和函数变量对象,分别对应全局上下文和函数上下文
全局变量对象
就是全局变量对象,浏览器中是 window
函数变量对象
上文提到过,此变量一般称为活动变量 (AO)
变量对象的初始化
在最初创建时只有 Argument
进入执行上下文(开始执行)
此时,函数AO将会将下面所有的东西都包含进去
- 函数的所有形参 (arguments)
- 声明的变量:由名称和对应值(undefind)构成,若变量对象已经存在相同名称的属性,则完全替换这个属性
- 声明的函数:由名称和对应函数 (function object)构成,如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性
例如:
function func(a) {
var b = function () {
/* codes */
}
function c() {
/* codes */
}
var d = 0
e = 0
}
func(1);
AO = {
arguments : {
0: 1,
length: 1
},
a: 1,
b: undefind,
c: [reference to function c(){}],
d: undefind,
e: undefind
}
代码执行
在代码执行时,会根据代码一步一步的将AO中的变量赋值
AO = {
arguments: {
0: 1,
lenght: 1
},
a: 1,
b: [reference to FunctionExpression],
c: [reference to Function],
d: 0,
e: 0
}
总结一下
函数变量对象的周期
- 创建函数上下文:创建AO。此阶段AO只有 arguments 属性
- 进入执行上下文:将形参,变量声明,函数声明等加入 AO
- 执行代码:通过代码修改 AO 中具体的属性值
作用域链
作用域
作用域就是规定变量起作用的区域,规定了JavaScript如何查找变量,也规定了其访问权限。
JavaScript使用的是静态作用域,会在函数创建的时候,确定并复制给函数的 [[scope]] 属性。
[[scope]] 中的变量就是所有父变量的副本
例如
function foo () {
// ...
function bar () {
/* codes */
}
// ...
}
那么,这两个函数的作用域 [[ scope ]] 为:
foo.[[scope]] = [
globalContext.VO // 全局环境
]
bar.[[scope]] = [
fooContext.AO,
globalContext.VO
]
作用域的类型
静态:在函数创建之初就已经确定
动态:在函数执行时才确定
作用域链
作用域链是上下文中重要的组成部分,它规定了 JavaScript 如何查找变量。
在上下文创建时,作用域链即被创建。顺序正如文中开头提到的:
-
将函数的 [[scope]] 复制进去。
-
然后初始化AO
-
最后将AO复制进去。
完成后如下:
functionContext = {
Scope: [AO, [[scope]]],
AO:{ ... }
}
在查找变量时会按照 Scope 链中的顺序向下查找
参考资料: