您的位置:首页 > Web前端 > JavaScript

JavaScript作用域和执行环境

2016-03-27 14:17 603 查看
参考文章:

/article/1308011.html(不得不说汤姆大叔这几篇确实是好文呐!但是要数清晰程度下面这一个更加清晰明了

/article/1307616.html

还有知乎上的一篇解读:http://www.zhihu.com/question/36393048

/article/4780185.html

http://www.codeweblog.com/javascript-%E4%BD%9C%E7%94%A8%E5%9F%9F%E9%93%BE%E8%A7%A3%E6%9E%90/

http://blog.sina.com.cn/s/blog_5d64f7e3010172mk.html

http://blog.sina.com.cn/s/blog_5d64f7e3010172mk.html

以下内容均在对上述的参考文章理解的基础上撰写

打开浏览器,就会创建全局执行环境Global Execute Context,一直到关闭应用(也就是关闭浏览器)为止,全局的执行环境才会被销毁。

每个函数都有自己的执行环境,执行到函数B,就会创建新的执行环境B,然后压入栈内,当函数B执行完,则执行环境从栈内弹出,将控制权返回到之前的执行环境。

如果把全局的执行环境看做一个对象的话,那么这个全局的执行环境是这样的,包含三个部分:指针,变量对象,作用域链。

<span style="font-size:18px;">globalExecutionContext
{
this : 指向全局对象
globalVaribleObject : 全局的变量对象
scopeChain : 该全局执行环境的作用域链
}</span>


实际上,执行环境都会关联一个变量对象和作用域链

变量对象(Variable Object)是一个与执行环境相关的特殊对象,它存储在执行环境中声明的以下内容:

变量 : (var ,变量声明);

函数声明 : (FunctionDeclaration,缩写为FD);

函数的形参

作用域链稍后会讲到。

该全局执行环境有一个变量对象(variable object),特殊地是,由于这个变量对象属于全局执行环境,因此该变量对象是全局的。

<span style="font-size:18px;"><pre name="code" class="javascript">		var globalVar;
//创建函数
function outFun() {
var i = 0;
function innerFun() {
i++;
document.write(i);
}
return innerFun;
}
globalVar = outFun();//调用outFun函数,返回内部函数
globalVar();//调用内部函数,访问内部变量</span>



看上面这段代码:

在运行浏览器的时候,对应的全局变量对象是这个:

<span style="font-size:18px;">globalVO:
{
global : undefined,
outFun : function reference (闲置状态中)
}</span>


那这时候全局执行环境的作用域呢?当然只有Global Variable Object可以被访问,因为outFun创建的变量对象



<span style="font-size:18px;">scopeChain:
{
0 => globalVO
}</span>


当然了,outFun这个函数在创建的同时,有没有创建新的执行环境呢?当然没有!记住,执行环境只有在函数调用或执行的时候才会创建,并压入堆栈。也就是说到创建outFun这一步还在全局的执行环境中。

但是,outFun函数在创建的时候,却搞到了自己的变量对象,恩对,没错,创建即拥有自己的一块地盘,并且拥有自己的作用域链。当在outFun找不到要访问的变量的时候,就会往“父级”作用域中找,也就是说优先在当前变量对象中寻找。

这时候outFun的变量对象和作用域链为:

<span style="font-size:18px;">Vo:
{
i : undefined;//因为outFun还没运行,所以仅仅是声明在这,而并没有开始赋值
innerFun : function reference
}
scope chain:
{
0 => outFunAO (当前执行环境下的活动对象AO就是它)
1 => globalVO
}</span>


好了,继续往下走,调用outFun,返回值赋给globalVar,记住,从执行环境开始分析,然后深入VO和Scope chain

那么我刚刚提到了“调用outFun”,也就是说函数被调用了,开始创建新的执行环境,暂且命名为执行环境C,在执行环境C中,我们开始跑outFun函数里面的内容,此时我们应该分析什么?当然还是VO和Scope chain了。这时候outFun 的 VO要进化了,为什么呢,因为开始调用outFun VO里面的内容了,所以VO开始干活了,以前没有调用它的时候处于闲置状态,现在处于活动状态,而且还加了一个新的属性argument,所以要改名字了。我们称之为“Active
Object”,简称AO。恩,然后在新的执行环境C中outFun开始基于AO和scope chain(上面已经叙述过outFun的scope chain)干活。干完活之后结束执行环境C,顺便返回了一个内部对象innerFun的引用给了全局变量globalVar,然后顺利带着战利品回到全局的执行环境。

在执行环境C中,outFunAO为,然后进行outFun中的操作

<span style="font-size:18px;">outFun AO:
{
argument :undefined,//这里由于没有参数传入,所以为undefined,但是,argument其实是一个数组[]
i : undefined,
innerFun : function reference
}</span>


那么,这一步结束之后,global VO有没有变化?

当然!global VO里面的globalVar之前只是声明在那里了,可还没赋值呢,所以到了这一步(赋值这一步),global VO变成下面这个(实际上就是global的值变了,也没啥)

globalVO:
{
global : function reference to innerFun,
outFun : function reference
}
scope chain:
{
0 => outFun Vo
1 => global VO
}


好了,赋值完毕!到下一步,但是这里要提一件事情,就是outFun虽然结束了,但是它在内部创建了一个闭包(返回了一个内部函数),这个内部函数是可以访问outFun的成员数学i的!但是outFun要销毁啊,这咋办,所以呢,又创建了一个闭包专门代替outFun的工作,见下图(来自http://www.codeweblog.com/javascript-%E4%BD%9C%E7%94%A8%E5%9F%9F%E9%93%BE%E8%A7%A3%E6%9E%90/),assignEvent是这个链接里面举得例子,在我这里把它换成outFun()就可以了。



然后到了最后一步,调用globalVar,这时候创建新的执行环境D,执行环境D中VO为

innerVO:
{
argument : undefined,//和之前一样,实际上argument是一个数组
}
scopeChain:
{
0 => innerVO
1 => outFunVO
2 => globalVO
}


以上说的可能有点乱,下面总结一下:

1、浏览器对象是一个大的执行环境——全局执行环境,每个函数在调用的时候都会创建新的执行环境,并入栈,执行完毕,出栈销毁

2、每个执行环境都会关联一个变量对象,用于存储局部变量、内部函数,每个执行环境同样有一条作用域链,这条作用域链由嵌套深度决定,优先查询当前执行环境下的变量对象,找不到,沿着作用域链往下找,一直到全局变量对象。

3、每调用一个函数,都创建新的执行环境,同时,该函数关联的变量对象激活,添加argument等属性转为活动对象,直到执行环境结束,然后销毁但是,如果创建了闭包,则会存在于内存中,因为还有活要干
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: