ECMA-262-3详解(2)变量对象

原文地址

作者的话

有很多文章已经对ECMAScript的核心概念做了详尽解读。本系列文章翻译自Dmitry Soshnikov的个人网站,相信不少人已经看过原文或者译文。原文简洁易懂并且严谨,条理清晰地阐明了所有JavaScript开发者不得不深入理解的ECMAScript核心概念。重复翻译的原因主要是为了个人收藏、整理之用。初次翻译,技巧拙劣,如有不足,请不吝赐教。

正文

  1. 介绍
  2. 数据声明
  3. 不同执行上下文中的变量对象

    1. 全局上下文中的变量对象
    2. 函数上下文中的变量对象
  4. 处理上下文代码的阶段

    1. 进入执行上下文
    2. 代码执行
  5. 关于变量
  6. 实现的特点:__parent__属性
  7. 结论

介绍

我们总是在程序中声明函数和变量,然后成功地用作构建我们的系统。但是,解释器怎样以及在哪里找到我们的数据(函数和变量)?当我们引用需要的对象的时候发生了什么?

许多ECMAScript开发者知道变量和执行上下文紧密相关。

var a = 10; // variable of the global context
 
(function () {
  var b = 20; // local variable of the function context
})();
  
alert(a); // 10
alert(b); // "b" is not defined

同时,许多程序员也知道当前版本(ES6之前,译者注)的标准的独立作用域是通过“函数”代码类型的执行上下文创建的。比如,和C/C++相反,ECMAScript中的for循环块不会创建本地上下文。

for (var k in {a: 1, b: 2}) {
  alert(k);
}
  
alert(k); // variable "k" still in scope even the loop is finished

让我们看看当我们声明数据时的所发生的细节。

数据声明

如果变量和执行上下文相关,那么应该知道变量的数据存在哪里以及如何获取它们。这个机制被称为变量对象

变量对象(简称:VO)是一个与执行上下文相关的特殊对象,它存储了在上下文中声明的:

  • 变量
  • 函数声明
  • 函数形参

注意,在ES5中,变量对象的概念已经被词法环境模式取代,可以在适当的章节找到它的详细描述。

示意性的举个例子,可以将变量对象表示成一个普通的ECMAScript对象:

VO = {};

正如我们所说,VO是执行上下文的属性:

activeExecutionContext = {
  VO: {
    // context data (var, FD, function arguments)
  }
};

只允许在全局上下文(全局对象就是变量对象的地方)中,对变量间接引用(通过VO的属性名)。对于其他上下文,对VO对象的直接引用是不可能的,这纯粹是实现机制。

当我们声明了一个变量或函数,除了使用我们的变量的名字和值创建VO的新属性,没别的了。
比如:

var a = 10;
 
function test(x) {
  var b = 20;
};
 
test(30);

相应的变量对象是这样:

// Variable object of the global context
VO(globalContext) = {
  a: 10,
  test: <reference to function>
};
  
// Variable object of the "test" function context
VO(test functionContext) = {
  x: 30,
  b: 20
};

但是在实现层面(以及规范)中,变量对象是一个抽象的概念。物理上,在具体的执行上下文中,VO被叫做不同的名称,有不同的初始结构。

不同执行上下文中的变量对象

对于所有的执行上下文类型,变量对象的一些操作(比如变量实例化)和行为是共通的。从这点看,将变量对象看作是抽象的基础的东西很便利。函数上下文也可以定义与变量对象相关的额外内容。

AbstractVO (generic behavior of the variable instantiation process)
 
  ║
  ╠══> GlobalContextVO
  ║        (VO === this === global)
  ║
  ╚══> FunctionContextVO
           (VO === AO, <arguments> object and <formal parameters> are added)

我们详细解读一下。

全局上下文的变量对象

现在,应该先给出全局对象的定义。

全局对象是在进入任何执行上下文前创建的对象;这个对象仅存在一份,它的属性可以在程序的任何位置访问到,
全局对象的生命周期伴随着程序结束而结束。

在创建时,全局对象使用诸如:Math、String、Date、parseInt等初始化,也可以使用附加对象初始化,附加对象可以是全局对象本身的引用–比如在BOM中,全局对象的window属性引用全局对象(不是在所有的实现中都是如此)。

global = {
  Math: <...>,
  String: <...>
  ...
  ...
  window: global
};

当引用全局对象的属性,前缀往往可以省略,因为全局对象不能通过名字直接访问到。然而,在全局上下文中可以通过this值访问到它,也可以通过对它的递归引用访问到它,比如BOM中的window,因此可以简写:

String(10); // means global.String(10);
 
// with prefixes
window.a = 10; // === global.window.a = 10 === global.a = 10;
this.b = 20; // global.b = 20;

所以回到全局上下文的变量对象,这里变量对象就是全局对象本身:

VO(globalContext) === global;

很有必要理解这个事实,由于这个原因,在全局上下文中声明一个变量,我们可以通过全局对象的属性间接访问它(比如,当变量名事先未知时):

var a = new String('test');
 
alert(a); // directly, is found in VO(globalContext): "test"
 
alert(window['a']); // indirectly via global === VO(globalContext): "test"
alert(a === this.a); // true
  
var aKey = 'a';
alert(window[aKey]); // indirectly, with dynamic property name: "test"

函数上下文的变量对象

    原文作者:freepoix
    原文地址: https://segmentfault.com/a/1190000015972430
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞