概述
JavaScript 的可执行代码,具有执行上下文,而每个上下文包括以下 3 个属性:
- 变量对象(variable object, 简称 VO)
- 作用域链(scope chain)
- this
变量对象提供了当前环境所需的变量和函数
作用域链用于保证 JS 中变量和函数有序地访问
this 为函数提供了执行者对象
一个上下文的执行周期可以用下图示意:
本文就来介绍执行上下文中的变量对象
。
那什么是变量对象呢?先看定义:
变量对象是与执行上下文相关的数据作用域,用于存储执行上下文中的变量和函数声明。
不同的执行上下文,变量对象会有一些差别。接下来就分别针对不同的上下文讨论其区别。
一、全局上下文
全局对象(Global object) 是在进入任何执行上下文之前就已经创建了的对象;
这个对象只存在一份,它的属性在程序中任何地方都可以访问,全局对象的生命周期终止于程序退出那一刻。
在全局代码的上下文执行环境中,变量对象就是全局对象,在浏览器中,就是 window 对象。
此时,我们可以用 this 和 self 来访问到全局对象,也就是它本身
console.log(this) // window
console.log(self) // window
其次,全局对象初始创建阶段将 Math、String、Date、parseInt 等函数作为自身方法,还会把全局变量作为自己的属性。
用伪代码表示就是:
global = {
Math: <...>
Date: <...>
window: global // 引用自身
}
二、函数上下文
我们已经知道,变量对象存储量执行上下文中的函数声明和变量,在函数上下文中,多了 arguments(函数参数列表)
, 一个类数组对象。
用伪代码来表示:
VO = {
arguments: Arguments,
variables: undefine,
functionName: <Function reference>
}
函数未进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变为了活动对象(activation object)。
所以,在函数上下文中,我们将活动对象(activation object)作为变量对象,活动对象最开始只包含一个变量就是 arguments 对象(这个对象是全局环境中没有的)。
arguments 的属性值 Arguments 它包括如下属性:
- callee — 谁调用了本函数
- length — 真正传递的参数个数
- properties-indexes (字符串类型的整数) 属性的值就是函数的参数值(按参数列表从左到右排列)
三、创建过程
我们再一次来看这个过程图:
创建阶段
全局对象初始化的时候,就将变量对象引用了自身。
而函数的创建却有需要注意的地方。
函数在创建阶段就创建了变量对象
其中,变量对象包括:
- 当前函数的参数列表,建立 Arguments 对象。
- 所有的函数声明(不包括函数表达式哦!),直接指向函数
- 所有的变量声明(var 声明的变量),默认为 undefined
进入执行上下文时,函数声明和变量声明都会提前,这就是声明提升,但是变量声明的值都是undefined,而函数声明的变量已经可以指向函数。变量声明的优先级最低。
看下面这段代码:
function foo(a, b) {
var c = 10;
function d() {}
var e = function _e() {};
(function x() {});
}
foo(10);
当进入函数 foo 时,其变量对象的表现形式为:
VO = {
arguments: {
0: 10,
1: undefined,
length: 1
}
c: undefined,
d: <function reference to d>,
e: undefined,
}
x 是函数表达式,所以不在变量对象当中,e 变量引用的值也是函数表达式,所以变量 e 本身是声明,所以在变量对象当中。
执行阶段
当前进入执行阶段,变量对象激活成活动对象,函数会顺序执行代码,改变变量对象的值:
以上代码就变成:
AO = {
arguments: {
0: 10,
1: undefined,
length: 1
}
c: 10,
d: <reference to function declaration d>,
e: <reference to Function expression to _e>,
}
接下来看一段代码:
console.log(foo);
function foo() {
console.log("123")
}
var foo = "456";
以上会打印函数,是因为:
变量优先处理函数声明,再是变量声明。
再看一段代码:
if (true) {
var a = 1;
} else {
var b = 2;
}
console.log(a); // 1
console.log(b); // undefined
虽然 else 中的代码永远不会被执行,但是 b 的变量声明在执行之前就默认被设置成 undefined了。
总结
执行上下文包括三个属性,变量对象,作用域链,this, 不同的执行上下文,变量对象是有区别的。
全局上下文中,变量对象就是本身。
函数上下文中,变量对象包括:arguments, 函数声明,变量声明。在函数创建阶段,变量对象有默认值,进入执行阶段后,变量对象会被激活成活动对象,然后变量对象的值被顺序改变。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。