当 JavaScript 遇到可执行代码时,都会创建执行上下文。
每个执行上下文都有三个重要属性
- 变量对象(Variable object,VO)
- 作用域链(Scope chain)
- this
变量对象是与执行上下文相关的作用域,定义了在上下文中的变量和函数声明。
因为不同执行上下文下的变量对象稍有不同,所以我们来聊聊全局上下文下的变量对象和函数上下文下的变量对象。
全局上下文:
1.可以通过 this 引用,在客户端 JavaScript 中,全局对象就是 Window 对象。
2.全局对象是由 Object 构造函数实例化的一个对象。
3.预定义了一堆,嗯,一大堆函数和属性。
```javaScript
// 都能生效
console.log(Math.random());
console.log(this.Math.random());
```
4.作为全局变量的宿主。
```javaScript
var a = 1;
console.log(this.a);
```
5.客户端 JavaScript 中,全局对象有 window 属性指向自身。
```javaScript
var a = 1;
console.log(window.a);
this.window.b = 2;
console.log(this.b);
```
函数上下文
在函数上下文中,我们用活动对象(activation object, AO)来表示变量对象。
活动对象,是在进入函数上下文时创建的,它通过函数的 arguments 属性初始化。arguments 属性值是 Arguments 对象。
执行过程
执行上下文可以分为两个阶段: 进入上下文,和执行代码
进入执行上下文
当进入执行上下文时,这时候还没有执行代码,
变量对象会包括:
-
函数的所有形参(如果是函数的执行上下文)
- 由名称和变量值组成的一个变量对象的属性被创建
- 如果没有实参,属性值设为 undefined
-
函数声明
- 由名称和对应值(函数对象(function object))组成一个变量对象的属性被创建
- 如果重复声明此变量,后面的会覆盖前面的
-
变量声明
- 由名称和对应值组成一个变量对象的属性被创建
- 如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性
function foo(a) {
var b = 2;
function c() {}
var d = function() {};
b = 3;
}
foo(1);
//对应的执行上下文是这样的
AO: {
arguments: {
0: 1,
length: 1
},
a: 1,
b: undefined,
c: reference to function c(){},
d: undefined //因为它是变量声明的形式
}
执行代码
在代码执行阶段,会顺序执行代码,根据代码,修改变量对象的值
还是上面的例子,当代码执行完后,这时候的 AO 是:
AO = {
arguments: {
0: 1,
length: 1
},
a: 1,
b: 3,
c: reference to function c(){},
d: reference to FunctionExpression "d"
}
总结:
全局上下文的变量对象初始化是全局对象
函数对象的初始化只包括 Arguments 对象
当进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值
在代码执行阶段,会再次修改变量对象的属性值
第一题:
function foo() {
console.log(foo);
foo = 1;
}
foo(); //???
function bar() {
a = 1;
console.log(a);
}
bar(); //???
第二题: foo打印出来是个函数,根据上方第三点可知,这是因为在进入执行上下文时,首先会处理函数声明,其次会处理变量声明,如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性
console.log(foo);
function foo(){
console.log("foo");
}
var foo = 1;