正如你所见,执行上下文在逻辑上是一个栈(stack)。 首先可能有一段全局代码,它拥有属于自己的执行上下文; 在这段代码中可能调用一个函数,这个函数同样拥有属于自己的执行上下文; 这个函数可能调用另一个函数,等等。 即使当函数递归调用自己时,在每一步调用中仍然进入了不同的执行上下文。
1.3、活化对象和变量对象 | Activation object / Variable object
每一个执行上下文都有一个与之相关联的变量对象(Variable object)。 和它相似的,变量对象也是一个抽象实体,一种用来描述变量实例化的机制。 而有趣的是,在一段源代码中声明的变量和函数事实上被作为变量对象(Variable object)的属性(properties)而添加到变量对象中。
当控制进入了全局代码的执行上下文时,一个全局对象被用作变量对象。 这恰恰是为什么全局声明的变量和函数变成一个全局对象的属性的原因:
复制代码 代码如下:
var GLOBAL_OBJECT = this;
var foo = 1;
GLOBAL_OBJECT.foo; // 1
function bar() {};
typeof GLOBAL_OBJECT.bar; // "function"
GLOBAL_OBJECT.bar === bar; // true
Ok, 所以全局变量成了全局函数的属性,那么局部变量——那些在函数代码(Function code)中声明的变量呢? 事实上那很简单:他们也成了变量对象的属性。 唯一的区别是,在函数代码中,变量对象不是一个全局对象, 而是一个我们称之为活化对象(Activation object)。 每次进入函数代码的执行上下文时都会创建一个活化对象。
并非只有在函数代码中声明的变量和函数才成为活化对象的属性: 函数的每一个实参(arguments,以各自相对应的形参的名字为属性名), 以及一个特殊的Arguments对象(以arguments为属性名)同样成为了活化对象的属性。 需要注意的是,活化对象作为一个内部的机制事实上不能被程序代码所访问。
复制代码 代码如下:
(function(foo) {
var bar = 2;
function baz() {};
/*
在抽象的过程中,
特殊的'arguments'对象变成了所在函数的活化对象的属性:
ACTIVATION_OBJECT.arguments = arguments;
...参数'foo‘也是一样:
ACTIVATION_OBJECT.foo; // 1
...变量'bar'也是一样:
ACTIVATION_OBJECT.bar; // 2
...函数'baz'也是一样:
typeof ACTIVATION_OBJECT.baz; // "function"
*/
}) (1);
最后,Eval code 中声明的变量成为了上下文的变量对象(context's Variable object)的属性。 Eval code 简单地使用在它调用中的执行上下文的变量对象。
复制代码 代码如下:
var GLOBAL_OBJECT = this;
eval('var foo = 1');
GLOBAL_OBJECT.foo; // 1;
(function() {
eval('var bar = 2');
/*
在抽象过程中
ACTIVATION_OBJECT.bar; // 2
*/
}) ();
1.4、属性的内部属性 | Property attributes
就要接近主题了。 现在我们明确了变量发生了什么(它们成了属性),剩下的需要理解的概念就是属性的内部属性(property attributes)。 每一个属性拥有零至多个如内部属性——*ReadOnly,DontEnum,DontDelete和Internal**。 你可以把它们想象为标签——一个属性可能拥有也可能没有某个特殊的内部属性。 在今天的讨论中,我们所感兴趣的是 DontDelete。
当声明变量和函数时,它们成为了变量对象(Variable object)——要么是活化对象(在函数代码中), 要么是全局对象(在全局代码中)——的属性,这些属性伴随生成了内部属性 DontDelete。 然而,任何显式/隐式赋值的属性不生成 DontDelete。 而这就是本质上为什么我们能删除一些属性而不能删除其他的原因。
复制代码 代码如下:
var GLOBAL_OBJECT = this;
/* 'foo'是全局对象的一个属性,
它通过变量声明而生成,因此拥有内部属性DontDelete
这就是为什么它不能被删除*/
var foo = 1;
delete foo; // false
typeof foo; // "number"
/* 'bar'是全局对象的一个属性,
它通过变量声明而生成,因此拥有DontDelete子
这就是为什么它同样不能被删除*/
function bar() {};
delete bar; // false
typeof bar; // "function"