Js系列四:变量对象

在前面的文章中我们提到过变量对象(Variable Object),我们在Js代码中申明的所有变量都保存在变量对象当中,除此之外变量对象中还可能包含以下内容。

(1)函数的参数(arguments,es6中废弃)。

(2)当前执行上下文的所有函数申明(通过function申明的函数)。

(3)当前上下文的所有变量申明(通过var申明的变量)。

一,创建过程

变量对象的创建依次经历了一下过程

1,在chrome浏览器中,变量对象会首先获得函数的参数及其值,在Firefox浏览器中,是直接将参数对象arguments保存在变量对象中。

2,依次获取当前执行上下文的所有函数申明,也就是function申明的函数,在变量对象中会以函数名建立一个与之相对应的属性,属性值为指向所在内存地址的引用,如果函数名的属性已经存在,那么属性的值会被新的引用覆盖。

3,依次获取当前执行上下文的所有变量申明,也就是使用var关键字申明的变量。每找到一个申明就在变量对象当中以变量名建立一个属性,属性值为undefined,如果该变量的属性已经存在,为了防止同名函数被修改为undefined,则会直接跳过去,原属性值不会被修改

ES6支持新的变量申明方式,规则与var完全不同,他们是在上下文执行阶段开始执行的,避免了变量提升带来的一系列问题。

我们知道了上面的规则之后,我们思考一个问题,当我们执行上下文代码的时候具体的执行过程是怎么样的呢?


var a=20;


首先上下文阶段会先确认变量对象,而变量对象的创建过程则是先获取变量名并赋值为undefined,因此第一步是:


var a = undefined;


上下文的创建阶段完毕后,开始进入执行阶段,在执行阶段需要完成变量的赋值工作,因此第二步是:


a =30;


需要注意的是,这两步分别是在上下文的创建阶段与执行阶段完成的因此var a=undefined这一步其实提前到了比较早的地方取执行,下面通过一个简单的列子来证明。


console.log(a);//这个时候的a输出的结果为undefined。

var a = 30;


结合之前的解释,这个列子的实际执行顺序为


//c创建阶段

var a = undefined;

//执行阶段

console.log(a);

a=30;


这种现象被称为变量提升(Hoisting).

从上面的例子我们可以看出,在变量对象创建的过程当中,函数申明的执行优先级会比变量的优先级高一些,而且同名的函数会覆盖函数与变量,同名的变量不会覆盖函数。但在上下文的执行阶段,同名函数会被变量重新赋值。


var a=20;

function fn(){console.log('fn')};

function fn(){console.log('conver fn')};

function fn(){console.log('conver a')};

consle.log(a);

fn();

var fn = 'I vant conver function named fn';

console.log(fn);

//20;

//conver a

//I vant conver function named fn


上面代码的执行顺序为


//创建阶段

function fn(){console.log('fn')};

function fn(){console.log('conver fn')};

function fn(){console.log('conver a')};

var a =undefined;

var fn=undefined;

//执行阶段

a=20;

consle.log(a);

fn();

fn='I vant conver function named fn';

consle.log(fn);


根据输出结果可以证明,在创建阶段,后创建的函数会覆盖前面创建的函数,但是后创建的变量fn并没有覆盖前面创建的函数fn。而在执行阶段,a与fn的重新赋值导致他们发生了变化。

二,实列分析

为了更加深刻的理解变量对象,下面结合一些简单的列子来进行探讨。


//demo1

function test(){

    console.log(a);

    console.log(foo());

    var a=1;

    function foo(){

     return 2;

  }

}

test();


运行test函数时,对应的执行上下文开始创建,可以用如下形式来表示整个过程。


//c创建过程

testEC={

VO:{},//变量对象

scopeChain:[],//作用域链

this:{}

}

//这里我们暂时不分析作用域链与this,后续章节会详细讲解

//VO为Variable Object的缩写,既变量对象

VO={

arguments:{},

foo:<foo reference>,

a:undefined

}


在函数调用栈中,如果当前执行函数处于栈顶,则意味着当前执行上下文处于激活状态,此时变量对象称之为活动对象(AO,Activation Object)。活动对象包含变量对象的所有属性,并且此时所有的属性已经完成了赋值,除此之外,活动对象还包含了this指向。


//执行阶段

VO->AO

AO={

arguments:{...},

foo:<foo reference>,

a:1,

this:Window

}


我们可以通过断点调试的方式在chrome开发者工具中查看这一过程。

因此上面代码的实际实现顺序为


function test(){

   function foo(){

      return 2;  

}

var a=undefined;

console.log(a);

console.log(fn());

a=1;

}

test();


三,全局上下文的变量对象

以浏览器为列,全局对象为window对象。

全局上下文的变量对象有一个特殊的地方,既它的变量对象就是window对象,而且全局上下文的变量对象不能变成活动对象。


//以浏览器为列,全局对象为window。

//全局上下文

windowEC={

VO:window,

scopeChain:{},

this:Window,

}


除此之外,全局上下文的生命周期与程序的生命周期一样,只要程序运行不结束。全局上下文就会一直存在,其他的上下文环境都能直接访问到全局上下文的属性。

..............................................

下一章我们讲讲作用域与作用域链

..................................................

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值