上一篇简单介绍了执行上下文,如今来说讲与执行上下文密切相关的Variable Object;node
变量对象的定义:变量对象是一个特殊的对象,而且与执行上下文息息相关,VO(变量对象)里面会存有下列内容:浏览器
以上这些内容都会保存在变量对象中,而且变量对象又是(Execution Context)执行上下文的属性:bash
ExecutionContext = {
VariableObject : {
//这里保存了var声明的变量,FD,function arguments,以及函数形参
}
}
复制代码
下面的变量对象都用VO来代替闭包
只有全局上下文中的变量对象容许经过VO的属性名称间接访问(全局对象自身就是变量对象);对于其余的上下文直接去引用变量对象是不可能的,纯粹是一种内部的实现机制。稍后会有说明函数
当咱们声明一个变量或者函数的时候,就会成为VO对象新的属性,而且所声明的变量值会称为新的属性值,for example:学习
var a = 5;
function foo(x) {
var b = 10;
}
foo(20);
复制代码
上面说过每个执行上下文中都会有一个变量对象:ui
全局执行上下文中的变量对象
VO(globalContext) = {
a: 5;
foo: reference function foo
}
foo函数执行上下文中的变量对象
VO(foo function context) ={
x: 20,
b: 10,
arguments: {
0:20,
length:1,
callee: <reference> function foo
}
}
复制代码
在不一样执行上下文中的变量对象 函数上下文中能够在VO上定义额外的细节(例如arguments) spa
先来看看全局对象的定义:全局对象在进入任何执行上下文中就会被建立,该对象的属性能够在单一副本程序的任何地方访问到它,全局对象的生命周期是随着程序的运行结束而结束;3d
全局对象在被建立的时候会初始化包含下列属性,例如Math,String,Date等等;一样的也会建立一个额外的对象指向自身,例如在浏览器对象模型中,会建立一个window对象而且指向global object;在node.js中就是指的global对象code
global = {
Math: <...>,
String: <...>,
.....
.....
window: global
}
复制代码
例如:
var a = 6;
console.log(a) //6
console.log(window.a) //6
console.log(a === window.a) //true
复制代码
以前已经说过在全局执行上下文中的VO(变量对象)就是全局对象
VO(globalContext) === global;
复制代码
以前也说过在全局执行上下文中声明的变量,能够间接的经过global object的属性来得到:
var a = 'hello';
console.log(a) //'hello',这是直接在全局上下文中找到的
console.log(window.a) //'hello',这是间接经过global找到的,由于在浏览器中,会有一个window对象,而且仍是指向global
复制代码
在node.js中,能够看到变量是挂载到全局对象global上面的:
函数上下文中的(VO)变量对象是不能直接得到的,就好像在学习原型的时候,[[proto]]属性也是不能直接获取的,只不过浏览器厂商使用__proto__属性来模拟它;当函数被调用时,函数上下文中的VO也成为AO(活动对象)。
VO(functionContext) === AO
复制代码
当进入函数上下文,AO就会被建立,而且会被arguments属性所初始化,arguments的值指向一个对象。
AO = {
arguments: object
}
复制代码
其中arguments对象将会包含如下属性:
举例说明:
function foo(a,b,c) {
console.log(arguments)
console.log(arguments.length === 2) //true
console.log(foo.length) //3 注意与上一行进行区分
console.log(arguments.callee === foo) //true
}
foo(1,2);
复制代码
上下文代码处理过程分为两个阶段
函数上下文和全局上下文都会有这两个阶段,而且相互独立,互不影响。
第一阶段:进入执行上下文,可是尚未开始执行该上下文中的代码 在进入执行上下文,可是尚未开始执行该上下文中的代码的时候,VO会被下列属性所初始化:
function fn(a,b,c) {
}
foo(1,2)
进入函数执行上下文,可是尚未开始执行函数体里面的代码
VO={
a:1,
b:2,
c: undefined, //在函数调用时,并无给参数c传递值,那么就会被赋予为undefined
arguments: {
0:1,
1:2,
length:2,
callee: <reference> function fn
}
}
复制代码
2.对于每个函数声明,也会保存在VO中,可是以后的同名函数声明会覆盖以前的,也就是说VO中会保存最新的函数声明,下列举例说明:
function foo(a,b) {
console.log(a+b)
}
function foo(a,b,c) {
console.log(a+b+c)
}
当进入全局执行上下文时,VO会进行初始化,第二个同名函数声明会覆盖以前的函数声明
VO = {
foo: reference <function foo(a,b,c) {console.log(a+b+c)}>
}
复制代码
function fn(a,b,c) {
var d = 30;
function foo(){};
var test = function {}
}
fn(1,2);
当进入函数fn的上下文时,VO会进行初始化
VO = {
a: 1,
b: 2,
c: undefined,
foo: reference function foo,
d: undefined,
test: undefined,
arguments: {
0:1,
1:2,
length:2,
callee: <reference> function fn
}
}
复制代码
注意:若是进入函数上下文中会先检查形参,而后是函数声明(不是函数表达式),最后是其余变量
第二阶段:代码执行阶段 到这里为止,VO已经初始化了一些属性,可是仍是有一部分值并非咱们所但愿的(初始值被赋予了undefined)。 随着代码的执行,VO中属性的值会逐渐被修改。
上述例子中的VO会被修改为:
VO = {
a: 1,
b: 2,
c: undefined,
foo: reference function foo,
d: 30,
test: <reference> function,
arguments: {
0:1,
1:2,
length:2,
callee: <reference> function fn
}
}
复制代码
下面举一个经典的例子
alert(a); //function
var a = 1;
alert(a); //1
a = 10;
function a() {};
alert(a); //10
复制代码
为何第一个a的值为function,不是1或10或undefined? 分析过程:
此时VO = {
a: reference function a //这里并非undefined,不会被覆盖
}
复制代码
2.代码执行阶段——当上述代码执行到第三行时,VO会被修改为:
VO = {
a: 1
}
故a弹出来为1
复制代码
3.当代码执行到倒数第二行时,VO会被修改为:
VO = {
a: 10
}
故a弹出来为10
复制代码
var a = 10;
function fn(){
console.log(a); //10
}
复制代码
咱们都知道a的值打印出来是10,函数内部能够访问函数外部的变量,可是函数外部却不能访问函数内部的变量(闭包除外),但你知道为何会这样吗?下一篇中的做用域将会为你解释这个疑惑