变量对象是与 执行上下文 相关的数据作用域,存储了在上下文中定义的 变量 和 函数声明。
因为不同执行上下文中的变量对象稍有不同,所以我们来聊聊 全局执行上下文 下的变量对象和 函数执行上下文 下的变量对象。
💡 全局执行上下文中的变量对象就是全局对象
我们先了解一个概念,叫全局对象。在 W3School 中也有介绍:
全局对象 是预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符。通过使用全局对象,可以访问所有其他所有预定义的对象、函数和属性。
在顶层 JavaScript 代码中,可以用关键字 this
引用全局对象。因为全局对象是作用域链的头,这意味着所有非限定性的变量和函数名都会作为该对象的属性来查询。
例如,当 JavaScript 代码引用 parseInt
函数时,它引用的是全局对象的 parseInt
属性,相当于 window.parseInt
。全局对象是作用域链的头,还意味着在顶层 JavaScript 代码中声明的所有变量都将成为全局对象的属性。
如果看的不是很懂的话,容我再来介绍下全局对象:
this
引用,在 JavaScript 中,全局对象就是 Window 对象console.log(this);// Window { ... }
console.log(this instanceof Object);// true
console.log(Math.random === this.Math.random);// trueconsole.log(Math.PI === this.Math.PI);// true
const a = 'foo';console.log(this.a);// foo
const a = 'foo';console.log(window.a);// 'foo'this.window.b = 'foo';console.log(this.b);// 'foo'
在函数执行上下文中,我们用 活动对象(Activation Object,AO)来表示变量对象。
活动对象 和 变量对象 其实是同一个东西,只是变量对象是规范上的或者说是引擎实现上的,不可在 JavaScript 环境中访问,只有到当进入一个执行上下文中,这个执行上下文的变量对象才会被激活,所以才叫 Activation Object ,而只有 被激活 的变量对象,也就是活动对象上的各种属性才能被访问。
活动对象是在进入函数执行上下文时刻被创建的,它通过函数的 arguments
属性初始化。arguments
属性值是 Arguments 对象。
执行上下文的代码会分成两个阶段进行处理:
当进入执行上下文时,这时候还没有执行代码,变量对象的创建,依次经历了以下几个过程:
undefined
function
关键字声明的函数undefined
undefined
,则会直接跳过,原属性值不会被修改)🌰 代码示例:
function foo(a) {var b = 2;function c() {}var d = function () {};b = 3;}
在进入执行上下文后,这时候的活动对象 AO 是:
AO = {arguments: {0: 1,length: 1},a: 1,b: undefined,c: reference to function() {},d: undefined}
在代码执行阶段,会根据代码,顺序执行代码,修改变量对象的值
还是上面的例子,当代码执行完后,这时候的 AO 是:
AO = {arguments: {0: 1,length: 1},a: 1,b: 3,c: reference to function c(){},d: reference to FunctionExpression "d"}
到这里变量对象的创建过程就介绍完了,让我们简洁的总结我们上述所说:
VO 和 AO 到底是什么关系?
未进入执行阶段之前,变量对象(VO:Variable Object)中的属性都不能访问。
但是进入执行阶段之后,活动对象(AO:Activation Object)被激活,里面的属性包括 VO、函数执行时传入的参数和 Arguments 对象都能被访问了,然后开始进行执行阶段的操作。
利用公式可以简单表述为:
AO = VO + function parameters + arguments