至简 · 变量对象、执行期上下文

   前几个礼拜没有好好的写文章,其实也有在写,很多都是草稿,因为,写着写着就发现,一些点还是不够通透,于是就又去开始学习了,循序渐进也挺好。经过了几个周的反思、积累和沉淀,emmm…是时候总结出自己脑子里的东西了,今天讲讲变量对象和执行期上下文,
    执行期上下文,这是一个比较不容易描述的点,但是又真实存在,对于执行期上下文,网上也有很多的理解,有通俗的认为执行期上下文就是一段可执行的代码段,这是从表层去理解,的确,执行期上下文是建立在一块可执行代码段的基础之上的,但是仅理解到此是不够的。
    个人的理解:执行期上下文是一个环境,它建立在一段可执行代码的基础上,并且只针对该段代码块,一般是函数内部,函数被创建时随之而生,它包含了变量对象,作用域链,以及this,函数执行时,就是在这一段执行期上下文中进行操作,而定义函数时呈现的代码部分是给人看的,执行期上下文是系统内部看的,也就是说,系统不会看代码写成什么样,只会看函数所对应的执行期上下文是什么样。
    变量对象(Variable Object)是一个比较底层的概念,因为它的存在只在函数执行的前一刻产生,存在于执行期上下文中,其内部包含了定义的属性,变量和方法。以window为例。window属于全局对象,其内部包含了已经定义的各种属性以及函数,例如Math、Date、toString()…等等,总之很多,他们可以通过this引用。除此之外,window还作为全局变量的宿主存在,作为window的属性,指向变量自身。例如我们在全局中定义一个a变量,既可以通过this直接访问,也可以通过window.a进行访问。说这么多其实就是以window打个样,window对象就相当于一个变量对象,只是它是全局的执行期上下文中的变量对象。
    为了区分全局上下文中的变量对象,与函数上下文中的变量对象,将全局的变量对象称为VO(Variable Object),而将函数上下文中的变量对象称为AO(Active Object),也叫活动对象。其实都是变量对象VO
好了,是时候展示真正装逼了…
有必要说说函数执行阶段,真正操作的是谁了.

以一段代码为例:

    function foo(a){
        var b = 2;
        function c(){}
        var d = function(){
            console.log('d');
        }
        b = 3;
    }
    foo(1);

foo函数是一段可执行的代码段,在函数执行阶段,有一样东西将被添加到ESC执行栈中,此时函数被激活,这样东西就是产生的一段关于自身的执行期上下文,暂且以fooContext为名。其内部存的变量对象VO也被激活AO,活动对象是在进入函数上下文时刻被创建的,它通过函数的 arguments 属性初始化。arguments 属性值是 Arguments 对象。AO内部保存着其内部声明的变量及方法。

函数执行前,执行期上下文初始化:

fooContext:{
	AO:{
		arguments:{
			0:1,
			length:1
		},
		a:1,//被arguments对象初始化了
		b:undefined,
		c:reference to function c(){},
		d:undefined
	}
}

这段执行期上下文就是函数执行前一刻所产生的。
到代码执行阶段,操作的就是这段执行期上下文,不会再去看代码是怎么写的了!但是你也许会问,这玩意是怎么生成的啊,万一有变量名字一样的怎么办,并且万一变量名和函数名也一样怎么办,问的好!!!

送你三个锦囊妙计,用以生成执行期上下文之需:

  1. 首先获取函数的所有形参,以及被在函数内被var关键字声明的变量,将它们当作AO的属性挂载到AO对象上,值为undefined。
  2. 形参实参相统一,就是通过arguemnts对象初始化之前挂载的变量,有值的赋值,没值为undefined。像之前的例子中foo(1)中的实参值为1,所以AO中的a值就为1.
  3. 获取所有function关键字声明的函数,并将其函数名作为属性名挂载到AO中,值就为对应的函数。如果存在有变量名相同的情况,哼哼,直接覆盖。
    有此三计,保你无忧,额当然要是有些比较2的人,直接打印一个不声明的变量,唉,没办法,也说说吧。
    两种情况:
    还是以之前的为例;
    1.直接打印未声明,未赋值的变量。
	console.log(e);//直接报错e is not defined,
	e = 1;//赋值也不知道给谁赋,这玩意是谁啊,不认识。而且写在这有毛线用,上一句都报错了。想赋也赋不到啊

2.直接打印未声明,但是赋值了的变量。

	e = 1;//虽然未声明,但是会被隐式的当作window对象的属性
	console.log(e);//这里涉及到作用域,自己没有,可以向外部作用域查找,window有,所以打印1.

好了,接着之前的代码,说说函数执行阶段:

	开始赋值:
	b = 2;
	d = function(){  console.log('d');},
	b = 3;//之前的2被覆盖
	

执行结束后的执行其上下文:

fooContext:{
	AO:{
		arguments:{
			0:1,
			length:1
		},
		a:1,
		b:3,
		c:reference to function c(){},
		d:reference to FunctionExpression:d
	}
}

我们甚至可以观察函数执行的每一步的输出结果:

    function foo(a){
        console.log(b);
        var b = 2;
        console.log(b);
        function c(){}
        console.log(c);
        console.log(d);
        var d = function(){
            console.log('d');
        }
        console.log(d);
        b = 3;
        console.log(b);
    }
    foo(1);

分析分析:

  • 从第一个console开始看,第一个打印undefined。函数开始执行阶段,此时执行期上下文中AO的b为undefined,所以读取到的b也就为undefined。
  • 第二个打印2,函数执行了b=2,为b赋了新值2,此时执行期上下文中AO的b就变为2,所以从上下文中读取到的b也就为2.
  • 第三个打印c的函数体,因为执行期上下文中AO的c此时的值就是函数体本身。
  • 第四个打印为undefined,在这里要说明一点,用var声明和用function声明是不同的,function声明在上下文中的效果会直接将函数体作为属性值,直接赋值,而以var声明的,会被当作变量,变量只有在函数执行时,才会进行赋值操作。此处就是以var声明的,虽然值为函数,但依然被当作以变量声明的方式执行。
  • 第五个打印d等号后的函数体,也就是变量赋值。不要问我为什么不打印‘d’,因为函数没执行。
  • 第六个打印3,b被赋予新值,AO中的b的值也就改变。所以打印3。

在这里插入图片描述

最后再附上一幅图,以便于理解。

在这里插入图片描述
对于变量对象和执行期上下文就说到此处。
对于执行期上下文还涉及到一个作用域链的生成问题。开篇时提到了在执行期上下文中存在三个重要的点,变量对象,this,作用域链。打算下一篇就说明此处。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值