Variable Object(变量对象)

上一篇简单介绍了执行上下文,如今来说讲与执行上下文密切相关的Variable Object;node

变量对象的定义:变量对象是一个特殊的对象,而且与执行上下文息息相关,VO(变量对象)里面会存有下列内容:浏览器

  • variables(var,variableDeclaration,arguments);
  • function declarations(FD)
  • function formal parameters

以上这些内容都会保存在变量对象中,而且变量对象又是(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对象将会包含如下属性:

  • callee —— 指向当前函数
  • length——当函数调用时,传递过来的参数(实参)的数量;注意这里不是函数定义的时候参数的数量,function.length才是函数定义时候的参数数量
  • properties-indexes——就是函数调用时传递过来的值(实参)会以索引—属性的形式展示出来
举例说明:
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);
复制代码

上下文代码处理过程分为两个阶段

  1. 进入执行上下文,可是尚未开始执行该上下文中的代码(Entering the execution context,but the executable code has not started execute)
  2. 上下文中的代码执行阶段 (code execution)

函数上下文和全局上下文都会有这两个阶段,而且相互独立,互不影响。

第一阶段:进入执行上下文,可是尚未开始执行该上下文中的代码 在进入执行上下文,可是尚未开始执行该上下文中的代码的时候,VO会被下列属性所初始化:

  1. 对于每个函数的形参(若是进入了函数执行上下文)——函数的形参将会成为VO的属性,形参的值(函数调用时的传递过来的值)就会称为该属性的值,但对于没有传递过来的参数,其形参的值就会被赋予undefined,下列举例说明:
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)}>
}
复制代码
  1. 对于每个变量声明(函数表达式也是变量声明),所声明的变量将会称为VO的属性,其值会被赋予成undefined。可是若是变量名与以前的形参名或者函数声明时的函数名相同,那么以后的变量声明并不会破坏已经存在的属性(也即不会被后面的同名变量所覆盖)
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? 分析过程:

  1. 首先进入全局执行上下文时,会对VO进行初始化,此时因为在全局上下文中(就不会检查形参),会先检查函数声明,接着是变量声明;以前的第三条规则说过若是变量名与以前的形参名或者函数声明时的函数名相同,那么以后的变量声明并不会修改已经存在的属性(也即不会被后面的同名变量所覆盖)
此时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,函数内部能够访问函数外部的变量,可是函数外部却不能访问函数内部的变量(闭包除外),但你知道为何会这样吗?下一篇中的做用域将会为你解释这个疑惑

相关文章
相关标签/搜索