Skip to content

执行环境、作用域链和活动对象

Js执行环境

执行环境(execution context, EC)或者说执行上下文,是js中一个极为重要的概念。

执行环境分为三种:

  1. 全局执行环境
  2. 函数执行环境
  3. eval()执行环境

js为每一个执行环境都关联了一个变量对象。环境中定义的所有变量和函数都在保存在这个对象中。

EC的组成

当JavaScript代码执行的时候,就会进入不同的执行环境(执行上下文),这些执行环境会构成一个执行环境栈(执行上下文栈)(Execution context stack, ECS)。如下图:

ESC组成

变量对象

变量对象(VO):变量对象即包含变量的对象,除了我们无法访问它外,和普通对象没什么区别。变量对象存储了在上下文中定义的变量和函数声明

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

活动对象和变量对象其实是一个东西,只是变量对象是规范上的或者说是引擎实现上的,不可在 JavaScript 环境中访问,只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活,所以才叫 activation object ,而只有被激活的变量对象,也就是活动对象上的各种属性才能被访问。

活动对象是在进入函数执行环境时刻被创建的,它通过函数的 arguments 属性初始化。arguments 属性值是 Arguments 对象。

当一个函数被调用时,会建立一个称为执行环境的活动记录。这个记录包含函数是从何处(调用栈 —— call-stack)被调用的,函数是 如何 被调用的,被传递了什么参数等信息。这个记录的属性之一,就是在函数执行期间将被使用的 this 引用。

变量对象和活动对象的关系

未进入执行阶段之前,变量对象(VO)中的属性都不能访问!但是进入执行阶段之后,变量对象(VO)转变为了活动对象(AO),里面的属性都能被访问了,然后开始进行执行阶段的操作。 它们其实都是同一个对象,只是处于执行环境的不同生命周期。

AO 实际上是包含了 VO 的。因为除了 VO 之外,AO 还包含函数的 parameters,以及 arguments 这个特殊对象。也就是说 AO 的确是在进入到执行阶段的时候被激活,但是激活的除了 VO 之外,还包括函数执行时传入的参数和 arguments 这个特殊对象。

AO = VO + function parameters + arguments

执行环境分析

全局执行环境是最外围的执行环境,全局执行环境被认为是window对象,因此所有的全局变量和函数都作为window对象的属性和方法创建的。 js的执行顺序是根据函数的调用来决定的,当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈中。而在函数执行之后,栈将该函数的变量对象弹出,把控制权交给之前的执行环境变量对象。

代码:

js
var scope = "global"; 
function fn1(){
    return scope; 
}
function fn2(){
    return scope;
}
fn1();
fn2();

ESC流程

[[Scopr Chain]] 作用域链

理解:根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,这就是作用域链

js
var outerVar = "outer";
function fn(){
  console.log(outerVar);
}
fn();//result:outer

[[scope chain]]作用域链

解析:可以看到fn活动对象里并没有outerVar变量,于是沿着作用域链(scope chain)向后寻找,结果在全局变量对象里找到了outerVar,所以就返回全局变量对象里的outerVar值。

js
function outer(){
  var scope = "outer";
  function inner(){
    return scope;
  }
  return inner;
}
var fn = outer();
fn();

[[scope chain]]作用域链