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"
函数上下文中的变量对象在函数执行上下文中,VO是不能直接访问的,此时由激活对象(activation object,缩写为AO)扮演VO的角色。
VO(functionContext) === AO;
激活对象 是在进入函数上下文时刻被创建的,它通过函数的arguments属性初始化。grguments属性的值是Arguments object:
AO = { arguments: <ArgO> };
Arguments objects 是函数上下文里的激活对象中的内部对象,它包括下列属性:
callee — 指向当前函数的引用;
length — 真正传递的参数的个数;
properties-indexes (字符串类型的整数) 属性的值就是函数的参数值(按参数列表从左到右排列)。 properties-indexes内部元素的个数等于arguments.length. properties-indexes 的值和实际传递进来的参数之间是共享的。(译者注:共享与不共享的区别可以对比理解为引用传递与值传递的区别)
例如:
function foo(x, y, z) { alert(arguments.length); // 2 – quantity of passed arguments alert(arguments.callee === foo); // true alert(x === arguments[0]); // true alert(x); // 10 arguments[0] = 20; alert(x); // 20 x = 30; alert(arguments[0]); // 30 // however, for not passed argument z, // related index-property of the arguments // object is not shared z = 40; alert(arguments[2]); // undefined arguments[2] = 50; alert(z); // 40 } foo(10, 20);
最后一个例子的场景,在当前版本的Google Chrome浏览器里有一个bug — 即使没有传递参数z,z和arguments[2]仍然是共享的。(译者注:我试验了一下,在Chrome Ver4.1.249.1059版本,该bug仍然存在)
分阶段处理上下文代码现在我们终于触及到本文的核心内容。执行上下文的代码被分成两个基本的阶段来处理:
进入执行上下文;
执行代码;
变量对象的变化与这两个阶段紧密相关。
进入执行上下文当进入执行上下文(代码执行之前)时,VO已被下列属性填充满(这些都已经在前文描述过):
函数的所有形式参数(如果我们是在函数执行上下文中)
— 变量对象的一个属性,这个属性由一个形式参数的名称和值组成;如果没有对应传递实际参数,那么这个属性就由形式参数的名称和undefined值组成;
所有函数声明(FunctionDeclaration, FD)
—变量对象的一个属性,这个属性由一个函数对象(function-object)的名称和值组成;如果变量对象已经存在相同名称的属性,则完全替换这个属性。
所有变量声明(var, VariableDeclaration)
—变量对象的一个属性,这个属性由变量名称和undefined值组成;如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。
让我们看一个例子:
function test(a, b) { var c = 10; function d() {} var e = function _e() {}; (function x() {}); } test(10); // call
当进入“test”函数的上下文时(传递参数10),AO如下:
AO(test) = { a: 10, b: undefined, c: undefined, d: <reference to FunctionDeclaration "d"> e: undefined };
注意,AO里并不包含函数“x”。这是因为“x” 是一个函数表达式(FunctionExpression, 缩写为 FE) 而不是函数声明,函数表达式不会影响VO(译者注:这里的VO指的就是AO)。 不管怎样,函数“_e” 同样也是函数表达式,但是就像我们下面将看到的那样,因为它分配给了变量 “e”,所以它变成可以通过名称“e”来访问。 FunctionDeclaration 与 FunctionExpression 的不同,将在 Chapter 5. Functions进行详细的探讨。
这之后,将进入处理上下文代码的第二个阶段 — 执行代码。
执行代码这一刻,AO/VO 已经被属性(不过,并不是所有的属性都有值,大部分属性的值还是系统默认的初始值undefined )填满。
还是前面那个例子, AO/VO 在代码解释期间被修改如下:
AO['c'] = 10; AO['e'] = <reference to FunctionExpression "_e">;
再次注意,因为FunctionExpression“_e”保存到了已声明的变量“e”上,所以它仍然存在于内存中(译者注:就是还在AO/VO中的意思)。而FunctionExpression。未保存的函数表达式只有在它自己的定义或递归中才能被调用。 “x” 并不存在于AO/VO中。即,如果我们想尝试调用“x”函数,不管在函数定义之前还是之后,都会出现一个错误“x is not defined”
另一个经典例子:
alert(x); // function var x = 10; alert(x); // 10 x = 20; function x() {}; alert(x); // 20