正如我们从第二章.变量对象中了解到的,执行上下文的数据(变量,函数声明,函数形参)以变量对象的属性的方式储存。
我们也知道了变量对象在每次进入上下文阶段中创建和获得初始化值,然后在执行代码的阶段更新。
这一章着眼于和执行上下文直接相关的另一个细节——作用域链(scope chain)。
简而言之,作用域链(scope chain)主要和内部函数相关。
如我们所知,ECMAScript允许创建内部函数,并且这些函数甚至可以通过父函数返回。
var x = 10;
function foo() {
var y = 20;
function bar() {
alert(x+y);
}
return bar;
}
foo()();
我们也知道,每一个上下文都有它自己的变量对象:对于全局上下文是全局对象(global object)自身,对于函数上下文是活化对象(activation object)。
而作用域链实际上是内部上下文(inner context)的所有(父)变量对象的列表。这个链用于变量查找。以上面的例子而言,上下文“bar”的作用域链包括了:AO(bar),AO(foo)和VO(global)。
但让我们从细节上来分析一下这个主题。
我们从它的定义开始着手,然后通过例子来更深入地讨论一下:
作用域链(scope chain)是和某个执行上下文相关的所有变量对象的链式结构,它被用于在标示符解析(identifier resolution)中查找变量。
一个函数上下文的作用域链在函数调用时创建,由活化对象和这个函数的[[Scope]]内部属性组成。我们将在下面详细地讨论函数的这个[[Scope]]属性。
上下文的示意结构如下:
activeExecutionContext = {
VO: {...}, // AO
this: thisValue,
Scope: [
//作用域链,所有变量对象的列表,用于变量查找
]
};
其中的Scope定义为:
Scope = AO + [[Scope]]
例如,我们可以用一个普通的ECMAScript数组来表示Scope和[[Scope]]:
var Scope = [VO1, VO2, ..., VOn];//作用域链
作为替代结构,我们也可以将它表示为一个分层的对象链,链上的每一个节点都引用父作用域(父变量对象)。与这种视角相对应的是一些实现器(js引擎)中的parent概念(在第二章中有谈到):
var VO1 = {__parent__: null, ...other data}; -->
var VO2 = {__parent__: VO1, ... other data}; -->
// etc.
但用数组表示作用域链更方便一些,所以我们采用这种方式。而且,尽管在实现器的层面上可以使用涉及parent特性的分层链方式,规范中还是将它抽象地描述为“作用域链是一个若干对象的列表”。而数组正符合这一“列表”的概念。
这种AO + [[Scope]]的组合与后面我们将谈到的标示符解析(identifier resolution),都是和函数的生命周期相关的。
函数的生命周期分为两个阶段:创建阶段和活化(调用)阶段。让我们来看下这两个阶段的细节。
如我们所知,函数声明在进入上下文的阶段中成为变量/活化对象(VO/AO)的属性。让我们来看一个全局上下文中(VO == global object)变量和函数声明的例子:
var x = 10;
function foo() {
var y = 20;
alert(x+y);
}
foo(); // 30
在函数活化时,我们能看到正确的结果——30,这里有一个
这一章的第二部分是关于EMCAScript中的面向对象编程。在第一部分中我们讨论了OOP的基本理论并勾画出和ECMAScript的相似之处。在阅读第二部分之前,如果有必要,我还是建议首先阅读这一章的第一部分.基本理论,因为后面将会用到其中的一些术语。
这一章我们讨论ECMAScript中面向对象编程(object-oriented programming)的几个主要方面。由于这一主题已经在许多文章中谈论过,本章并不打算“老调重弹”,而是试图更多地着眼于这些过程内在的理论方面。尤其是,我们将研究对象创建的算法,看看对象间的关系(包括最基本的关系——继承)是如何实现的,并且给出一些讨论中将用到的准确定义(我希望这样能够打消一些术语和思路上的疑惑以及一些关于Javascript文章中OOP部分的常见的混淆)。
在这一章中我们来谈谈Javascript中被讨论最多的话题之一——关于闭包(closures)。事实上这个主题并不是新鲜的。然而我们在这里将试着更多从理论的角度去分析和理解它,然后我们还会看一下ECMAScript内关于闭包的内容。
在这章里我们讨论ECMAScript中的一个基本对象——函数。我们将会看到不同类型的函数如何影响一个上下文中的变量对象,以及这些函数的作用域链中都包含什么。我们将会回答像下面这样经常被问到的问题:“下面这两种创建函数的方式有什么区别吗(如果有的话,区别是什么呢)?”
正如我们从第二章.变量对象中了解到的,执行上下文的数据(变量,函数声明,函数形参)以变量对象的属性的方式储存。
许多程序员习惯于认为在编程语言中,this关键字是与面向对象编程紧密相关的,而且引用的是由构造式最新创建的对象。在ECMAScript中,这个概念也被实现了,然而我们将看到,在这里它不仅仅指向已创建的对象。
在程序中我们总是声明变量和函数然后用它们来搭建我们的系统。但是解释器(interpreter)是在哪里和以什么方式来找到我们的数据(函数,变量)的呢?
第1章:在这一章里,我们将会讨论ECMAScript中的执行上下文(execution context)以及与它们相关的可执行代码(executable code)的类型。