【ECMAScript-262-3】变量对象 Variable Object
JS中的VO(抽象概念)是个非常重要的概念,VO是上下文的属性,其中包含着当前上下文中的变量、函数声明(注意不是函数表达式)和函数形参,用来实现变量查找。
注:函数声明(Functiondeclaration->FD)指如下方式: function xxx(){}
函数表达式(Function expression->FE) 指的是位于表达式位置(expression position)的函数,最常见方式如下:var xxx = function(){} (not a NFE(named function expression)) .后续有详细介绍。FE不影响VO。
VO是当前活动上下文(EC)的属性,还记得EC栈吧?当时用数组表示的。VO用伪代码可以表示为一个对象:VO={};
VO:{
//data [var,FD,arguments]
}
};
在JS中,每一个上下文都有一个VO,在上下文中声明变量或函数就为VO添加了新的属性。可以直接访问的VO只有全局对象(global object),其他的上下文的VO不能直接访问。
--------
不同上下文的变量对象有不同的表现,下边分述:
全局上下文中的变量对象
全局对象的定义:此对象在进入任何执行上下文前就已存在,并且只有一份,他的属性在整个程序(program)级别都能访问,他的生命周期与程序相同。
全局对象初始化时就已包含属性如:Math,String等,其中一些对象指向全局对象自身,如window。
全局上下文的变量对象 === 全局对象 : VO(globalContext) === global。
这样我们就可以理解,为什么在全局上下文中声明一个变量,就可以间接访问它。
函数上下文中的变量对象
函数上下文的变量对象不能被直接访问,而且在这里为了便于区分,也改了个名字:activation object(激活对象?随便怎么翻译),简称AO。AO不能直接访问到,它也一样是抽象概念。
AO在进入函数上下文时创建,在进入时他就带有一个属性arguments,这个arguments就是我们平常用的那个,它包含属性callee,length等等。
arguments需要注意的就是他与实参的对应。如下函数:
//dosth...
}
xxx(1,2);
在第三个参数没有传递时,大家都知道z的值和arguments[2]的值都为undefined,但是,z与arguments[2]却由于调用没有为其传入值,而断掉了他们之间的关联:
例如为x赋值:
x = 5;
console.log(arguments[0]);//5
//or do this
arguments[1] = 10;
console.log(y);//10
//but
z = 15;
console.log(arguments[2]);//undefined;
arguments[2] = 20;
console.log(z);//15
}
xxx(1,2);
相当于没有传入参数就切断了arguments与对应的参数之间的关联。
处理上下文代码的阶段
包含进入上下文和代码执行两个阶段。VO的形成与修改与此两阶段密切相关。
1.进入上下文
这个阶段VO要添加的属性:
a.每个形参:名字+值,如果没有传递,则为 名字+undefined
b.每个函数声明:正常创建属性,如果有同名属性,则替换之,并修改原同名属性的attributes({DontDelete},{DontEnum}这些)
c.变量声明var:名字+undefined(如果有同名形参或函数,变量声明不影响已存在的属性,FE不影响VO)
2.代码执行阶段
此阶段VO和AO都已有属性,只是其值可能为undefined。
此阶段属性的值会在执行中被修改。
两个经典的例子:
var x = 10;
alert(x); // 10
x = 20;
function x() {}
alert(x); // 20
var a = 1;
} else {
var b = 2;
}
alert(a); // 1
alert(b); // undefined, 而不是 "b is not defined"
第二个例子中可以看到,b已经被添加到VO中并被赋值为undefined
关于变量
一直有个错误的想法,在函数中不用var定义一个变量(暂且这么称呼)就是定义了一个全局变量,并且为了这个还讨论过全局变量污染,但事实真的是这样吗?
事实却是:变量只能通过var关键字来声明!!!
不通过var关键字声明的“变量”其实是全局对象的一个属性。
alert(b); // "b" is not defined
b = 10;
var a = 20;
上边的代码能说明些情况吗?
并且,使用var声明的变量会包含特性:{DontDelete},这个也经常被拿来讨论,其结果用下面代码可以表示:
alert(window.a); // 10
alert(delete a); // true
alert(window.a); // undefined
var b = 20;
alert(window.b); // 20
alert(delete b); // false
alert(window.b); // still 20
看清楚他们之间的区别了吗?
还有一个不影响VO的就是Eval代码中的变量声明:
alert(window.a); // 10
alert(delete a); // true
alert(window.a); // undefined
有一点要特别注意,在Firebug中进行跟踪时,总会显示用var声明的变量是可以delete的,这可能是因为Firebug中代码的执行都是通过eval进行的。
参考资料:http://dmitrysoshnikov.com/ecmascript/chapter-2-variable-object/