在JavaScript中,变量对象(Variable Object, VO)是一个抽象概念,它在不同类型的执行上下文中扮演着存储变量和函数声明的角色。根据执行上下文的不同,变量对象的具体表现形式也会有所差异,主要分为两种情况:全局执行上下文和函数执行上下文。
1、在全局执行上下文中
-
全局变量对象:在全局执行上下文中,变量对象就是全局对象本身。在浏览器环境中,这个全局对象通常是
window对象(在Node.js环境中则是global对象)。这意味着在全局作用域中声明的任何变量或函数都会成为全局对象的属性。 -
初始化:当JavaScript引擎开始执行脚本时,它首先会创建一个全局执行上下文,并初始化变量对象。在这个阶段,所有的全局变量和函数声明都会被添加到变量对象中。此外,
this关键字在这个上下文中指向全局对象。 -
例子:
var globalVar = "I'm global"; function globalFunc() { console.log("I'm a global function"); } // 在浏览器环境中,以下判断为真: console.log(globalVar === window.globalVar); // true console.log(globalFunc === window.globalFunc); // true
2、在函数执行上下文中
-
活动对象(Activation Object, AO):在函数执行上下文中,变量对象被称为活动对象。活动对象是在函数被调用时创建的,它包含了函数的局部变量、参数、
arguments对象以及该函数内部的函数声明。 -
初始化:函数执行前,会进行如下步骤来准备活动对象:
- 创建一个新的空活动对象。
- 将函数的形参(如果有)复制到活动对象上,作为其属性。
- 将函数声明(使用
function关键字定义的函数)添加到活动对象上。注意,函数声明会被提升至活动对象的顶部,因此在函数体的任何位置都可以访问。 - 将变量声明(使用
var关键字声明的变量)添加到活动对象上。同样,变量声明也会被提升,但其初始值为undefined,直到赋值语句被执行。- 如果遇到变量声明(使用
var关键字)与已存在的形参或之前声明的变量重名,JavaScript的变量提升(hoisting)机制会确保变量声明被移动到作用域的顶部,但这种重命名实际上并不会改变已存在的形参或变量的引用。具体来说:-
- 形参与变量声明重名:如果在函数内部使用
var声明了一个与形参同名的变量,尽管由于变量提升看似该声明被移到了函数顶部,但实际上形参的值不会受到影响,因为形参的初始化发生在变量声明提升之前。换句话说,这种重命名实际上是无效的,形参仍然保留其传入的值。
- 形参与变量声明重名:如果在函数内部使用
-
- 函数声明与变量/形参重名:如果一个函数内部同时有函数声明和同名的变量声明或形参,根据ECMAScript规范,函数声明会覆盖任何同名的变量声明,包括形参。这是因为函数声明拥有更高的优先级。这意味着即使形参或变量先被声明或定义,后续的函数声明依然会“胜出”,导致之前的声明被忽略。
-
- 如果遇到变量声明(使用
-
变量名重复代码示例:
function example(param) {
var param; // 这里的var声明是多余的,不会改变param的值
console.log(param); // 输出传入的param值,形参未受影响
var anotherVar = "initial value";
function anotherVar() { // 函数声明覆盖了之前的变量声明
console.log("I'm a function now");
}
console.log(anotherVar); // 输出函数对象,而不是"initial value"
}
example("function argument");
- 函数执行上下文中活动对象代码示例:
function myFunction(a, b) { var x = 10; function innerFunc() { console.log("I'm an inner function"); } console.log(x); // 10 console.log(innerFunc); // [Function: innerFunc] console.log(arguments); // { '0': argValue1, '1': argValue2 } } myFunction("argValue1", "argValue2");
总结
- 全局上下文的变量对象直接关联到全局对象,存储全局变量和函数。
- 函数上下文的变量对象是活动对象,存储局部变量、函数参数、
arguments对象和内部函数声明,是在函数调用时动态创建的。