Javascript执行环境及作用域

一步一步重温 JavaScript 基础系列。
本章节将会理解JS的执行环境即执行上下文相关概念,包括变量对象、活动对象、以及两者区别

概念

执行环境(execution context)定义了变量或函数有权访问的其它数据,决定了各自的行为。当JS代码执行的时候,会进入不同的执行环境,这些不同的执行环境就构成了执行环境栈。当执行环境中的代码执行完毕之后,执行环境被销毁,其中的所有变量和函数也随之销毁。执行环境也称之为执行上下文。

每个执行环境都有三个重要的属性:变量对象(VO)、作用域链(scope chain)以及 this。

按值传递图解
注:对于VO来说,函数表达式不包含在VO中,没有使用var声明的变量也不包含在VO中,这种方式只是给Global添加了一个属性。

执行上下文类型

  • 全局执行上下文,只有一个,在浏览器中,通常为windows对象,所有的全局变量和函数都作为window对象的属性和方法存在。对于全局执行环境来说,当关闭网页或浏览器时,该环境被销毁;
  • 局部(函数)执行上下文 ,每个函数都有自己的执行环境,当js执行流进入一个函数时,函数的环境会被推入一个环境栈中,当函数执行完毕后,栈将其环境弹出,该环境随即被销毁;
  • Eval上下文,运行在 eval 函数中的代码,由于eval 的性能低、不易调试且有安全问题,一般禁止使用。

执行栈

执行栈,也称调用栈,具有“后进先出”(LIFO)原则,用于储存代码的执行期间创建的所有执行上下文。

首次运行js代码时,会创建一个全局执行上下文,并被push至执行栈首位。当顺序执行函数调用时,引擎都会为执行函数创建一个执行上下文,并push至当前执行栈的栈顶。

当函数调用完成后,其执行上下文就会从执行栈顶被推出(这是理想情况,闭包则不会,后续深入理解)。

执行上下文的生命周期

创建阶段

  • 创建变量环境(VO)
    ① 函数环境会初始化创建 Arguments对象(并赋值)
    ② 函数声明(并赋值)
    ③ 变量声明,函数表达式声明(未赋值)
  • 确定this指向(this由调用者确定)
  • 确定作用域(词法环境决定,哪里声明定义,就在哪里确定)

执行阶段

  • 变量对象赋值
    ① 变量赋值
    ② 函数表达式赋值
  • 调用函数
  • 顺序执行其它代码

代码解释如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/*
* 第一步:
* 当程序加载时全局执行环境就被创建,并且创建一个VO对象来保存当前全局变量与全局函数,
* 在刚刚创建的VO对象中,变量的值被保存为undefined,函数属性的值保存为对应的函数体
* 全局执行环境 ==> VO = {
a: undefined,
fn1: fn1函数体,
fn3: fn3函数体
}
*/

var a = "global";

/*
* 第二步:
* 当程序执行到此处时,变量a执行赋值操作,VO对象中的属性a的值发生变化
* 全局执行环境 ==> VO = {
a: "global",
fn1: fn1函数体,
fn3: fn3函数体
}
*/

function fn3(num){
   console.log(num);  
}
 
function fn1(num1,num2){
var b = num1 + num2;

function fn2(num3){
var c = ++num3;
fn3(c);
/*第五步*/
}  

fn2(b);
/*第四步*/
}
 
fn1(2,5);
/*
* 第三步:
* 发现fn1函数被调用并传参,那么就进入fn1函数内部并创建fn1执行环境,由于在函数执行环境中
* VO是无法直接访问的,所以当fn1环境被创建时,会同时创建一个AO对象,其默认带有一个arg属性
* 这个属性保存了函数调用时传入的实参,该AO对象同时也会保存fn1函数的形参num1,num2并赋值,
* 保存局部变量b,b的值为undefined, 函数属性的值保存为对应的函数体
* fn1执行环境 ==> AO = {
arguments: [2,5],
num1: 2,
num2: 5,
b= undefined,
fn2: fn2函数体
}
*/

总结
① 当程序开始执行,自动创建一个全局执行环境和一个VO对象;
② 当函数被调用时,在函数中会创建一个函数执行环境和一个AO对象,AO与VO功能相同;
③ 当函数执行环境被创建后,js会创建一个执行环境栈,将当前执行环境入栈,当该函数执行结束之后,其对应的执行环境会从栈中抛出并销毁。

变量对象(AO)和活动对象(VO)

变量对象(Variable Object,简称VO)

每个执行环境都有一个与之关联的变量对象,用来保存环境中定义的所有变量和函数。一般来说,VO对象包含变量、形参和函数声明。

活动对象(Activation Object,简称AO)

当进入到一个执行上下文后,这个变量对象才会被激活,所以叫活动对象(AO),这时候活动对象上的各种属性才能被访问。

函数的执行环境是在调用时创建的,在创建函数执行环境的同时会对应创建一个带有arguments属性的AO对象,该对象将代替VO对象来保存当前函数环境中的变量、参数和函数,所以在函数执行环境中VO就是AO。

VO和AO的区别

① 非函数执行环境对应创建一个VO对象;
② 函数执行环境对应创建一个默认带有arguments属性的AO对象;
③ AO是函数执行环境下的VO,两者的作用是相同的。

原创技术分享,您的支持将鼓励我继续创作