谈谈自己对js闭包,执行上下文,作用域链,活动对象AO,变量对象VO的理解

引子:关于闭包
什么是闭包呢?
  从定义上来看,所有的函数都可以是闭包。当一个函数在调用时,引用了不是自己作用域内定义的变量(通常称其为自由变量),则形成了闭包;闭包是代码块和创建该代码块的上下文中数据的结合。

例子:   function mytest( ){                     
           var test=10;
          return function( ){ 
                 test++;
               alert(test);
      } 
}
var atest = new mytest( ); //引用返回的函数
atest( );  // 11
atest( ); //  12
ps:注意运用闭包的常见错误-->for(var i,len=xx.length;i<len;i++)循环;当我们所引用的自由变量为i时,由于i一直在内存中没有释放,所以函数每次alert(i)时,其值均为最终的i;(例子很多,就不写了)
    通过测试结果我们可以发现, 闭包会使该函数引用的变量一直在内存中,原因是什么呢?
简单的解释就是因为这个返回的函数引用了变量test;当浏览器解析到var atest=new mytest();这一行且mytes()函数执行完毕,准备内存释放时,发现所返回的函数引用了test变量。从而该出栈的并没有出去;
    想完全弄清楚这个问题,我们还需要进一步理解AO(活动对象)和VO(变量对象)以及作用域链、执行上下文的问题。
    那么这整个机制浏览器到底是怎么解析的呢?
    不急,我们来看看执行上下文、作用域链、活动对象和变量对象;
写在前面的执行上下文:
 a:定义:

  每次当控制器转到ECMAScript可执行代码的时候,即会进入到一个执行上下文。执行上下文(简称-EC)是ECMA-262标准里的一个抽象概念,用于同可执行代码(executable code)概念进行区分。

活动的执行上下文组在逻辑上组成一个堆栈。堆栈底部永远都是全局上下文(global context),而顶部就是当前(活动的)执行上下文。堆栈在EC类型进入和退出上下文的时候被修改(进栈或出栈)。

b:若我们定义执行上下文堆栈是一个数组:

ECStack = [];

//GlobalContext始终在堆栈底部,其余的FunctionContext按激活顺序被压入,结束时被弹出

ECStack = [
  globalContext
];

ECStack = [  FunctionAaContext,//函数A内部函数a可执行代码

FunctionAContext,//函数A可执行代码(不包含内部函数代码,只能对其声明,就像在全局中只能对函数A声明一样,需要内部函数激活时,创建   GlobalContext                                                     出新的执行上下文环境,才能执行相应的代码)      调用结束后出栈  
];

ps:

每次进入function (即使function被递归调用或作为构造函数) 的时候或者内置的eval函数工作的时候,当前的执行上下文都会被压入堆栈;

1、作用域链
     首先,我们如何创建一个作用域呢,function()。函数是javascript中唯一一个能创建出作用域的,也就是说for、if、while的{}是不能创建出作用域的。区别c++中的块作用域{}。
     一个函数的作用域创建后,将贯穿他的始“{”,终“}”,作用域

在函数创建时被存储,与函数共存亡

。这句话就应该着重理解贯穿2字了,若函数内部嵌套着多个函数,那么从最内层函数作用域依次往外就形成了作用链。
ps:需要我们理解作用域链的变量查找机制是由内往外的。先找自身作用域,再一次往外,若没有,则等同没有var时的声明(为全局添加了一个属性);

作用域链正是内部上下文所有变量对象(包括父变量对象)的列表

2、变量对象(Variable Object)、活动对象(Activation Object)
     浏览器在对代码进行解析时,会先进行变量声明和函数声明以及函数形参声明;
(全局作用域下)那么这些优先声明的变量储存在哪里呢? 
没错,就在变量对象中(抽象概念);活动对象怎么理解呢?

抽象变量对象VO (变量初始化过程的一般行为)

--> 全局上下文变量对象GlobalContextVO
          (VO === this === global)

--> 函数上下文变量对象FunctionContextVO
           (VO === AO, 并且添加了<arguments>(形参类数组)和<formal parameters>(形参的值))

ps:在函数执行上下文中,VO是不能直接访问的,此时由活动对象扮演VO的角色。Arguments对象是活动对象的一个属性,它包括如下属性:

  1. callee — 指向当前函数的引用
  2. length — 真正传递的参数个数
  3. properties-indexes (字符串类型的整数) 属性的值就是函数的参数值(按参数列表从左到右排列)。 properties-indexes内部元素的个数等于arguments.length. properties-indexes 的值和实际传递进来的参数之间是共享的。

现在让我们来串一下

1、全局执行上下文

创建global.VO

2、全局变量的赋值 | 调用函数()(激活)

激活函数后,会得到当前的AO,其中有内部函数的声明、内部变量的声明、形参

3、进入所激活的函数的上下文

进行所在作用域链上的变量的赋值 各种运算 (作用域链包含全局的VO,和当前执行上下文的AO)

4.a、若在函数中有内部函数调用(或自执行),重复3;

4.b  若返回一个函数(或其引用),且该函数有对自由变量的引用-->形成闭包-->作用域链机制依然有效-->当前已压入执行上下文堆栈的FunctionContext不会出栈;-->回到2;

4.c  正常return或正常结束,FunctionContext出栈;-->回到2;

5.所有代码执行完毕,程序关闭,释放内存。

恳请大牛老鸟指出错误和理解不对的地方;大二学生一枚,努力学习中。。。   见谅见谅

纯手打,转载收藏不用注明出处,(估计也不会有,请忽略)。

写在最后:

  如果你是刚开始接触javascript,如果你也相信博客园能够让你的学习更加规划与深入,我推荐你看看王福鹏老师和汤姆大叔的博文,自己仔细揣摩,记录下自己的感想,一定会有收获的。加油。我正在努力看深入系列的设计模式,感觉看着不是很顺,有些不好理解,希望有经验的博主们能指点指点。这是我的第一篇博文,真心希望能够在博客园中寻到良师益友。

另贴上汤姆大叔深入javascript系列博文的地址,

http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html
时间: 2024-10-10 21:25:12

谈谈自己对js闭包,执行上下文,作用域链,活动对象AO,变量对象VO的理解的相关文章

js的执行上下文

js中代码有三种类型 : global , function , eval 每一种代码的执行都需要依赖自身的上下文环境 每种代码的执行(程序开始执行,函数被调用,eval代码执行)都会产生一个新的上下文环境,这个上下文环境就称为执行上下(execution context--EC) 执行上下文可以抽象的认为是一个Object,具有一系列属性,其大体结构如下: Execution Context : { variable object : [vars,function declaration,ar

函数之闭包的基础----作用域链

理解闭包我觉得需要事先了解下面的概念和知识: 作用域(链),变量对象(活动对象),执行环境. 依次解释概念: 作用域:我们知道变量的 作用域就是程序源代码中定义这个变量的区域.(犀牛书)分为 全局作用域 和 局部作用域.(重复之前的:函数参数也是局部变量,它们只在函数体内有定义.)函数定义是可以嵌套的,所以就会有函数作用域. 函数作用域:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的.就是说,在函数体定义了一个变量,这个变量在这个函数体内始终有定义,并且,嵌套在该函数里的嵌套

JS高级 -- 执行上下文与作用域链

这个问题涉及到三个点: 1. 执行上下文 2. 函数嵌套导致的执行上下文栈 3.闭包 1 <script> 2 var a = 1; 3 var f1 = function(){//第一个函数 4 var a = 2; 5 var b = 1; 6 7 var f2 = function(){//第二个函数 8 var c = 1; 9 var f3 = function(){//第三个函数 //第三个函数执行,他自己的执行上下文中没有a,b,c,则从父级函数f2的执行上下文中去找,f2中有c

作用域和闭包——执行上下文

console.log('clm'); //undefined var a = 10; fn('clm'); // 'clm' , 20 function fn(name){ age = 20; console.log(name,age); var age; } 执行上下文 范围:一段<script>或者一个函数 都会生成一个执行上下文 (一段<script>)全局:创建全局上下文,执行前,会先把变量定义.函数声明拿出来. (函数)函数:创建函数上下文,执行前,会先把变量定义.函数

谈谈我对JS闭包的理解

这一篇博客承接上一篇,如果大家没看上一篇,建议看看.....直通车..... 好吧,咱们一起来看看这个闭包,这次我们的重点并不是弄明白闭包是什么?而是搞清楚JS的闭包是怎么产生的.接着上一篇博客的示例: var a = function(x){ var b = 'bb'; var inner = function(){ var c = 'cc'; }; return b; }; 当a函数执行到给inner变量赋值匿名函数之后,形成下面的引用关系,直接复用上次博客的图: 从上图很容易看出,这时候i

js学习笔记之作用域链和闭包

在学习闭包之前我们很有必要先了解什么是作用域链 一.作用域链 作用域链是保证对执行环境有权访问的所有变量和函数的有序访问. 这句话其实还是蛮抽象的,但是通过下面一个例子,我们就能清楚的了解到作用域链了. 1 var color="blue"; 2 function changeColor(){ 3 var anotherColor="red"; 4 function swapColors(){ 5 var tempColor=anotherColor; 6 anot

关于JS里的函数作用域链的总结

在JavaScript中,函数的作用域链是一个很难理解的东西.这是因为JavaScript中函数的作用域链和其他语言比如C.C++中函数的作用域链相差甚远.本文详细解释了JavaScript中与函数的作用域链相关的知识,理解这些知识可以帮助你在处理闭包的时候避免一些可能出现的问题. 在JavaScript中,函数可以让你在一次调用中执行一系列的操作.有多种方式来定义一个函数,如下: 1.函数声明: function maximum(x, y) { if (x > y) return x; els

JS详细图解作用域链与闭包

JS详细图解作用域链与闭包 攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你是初入前端的朋友,我没有办法直观的告诉你闭包在实际开发中的无处不在,但是我可以告诉你,前端面试,必问闭包.面试官们常常用对闭包的了解程度来判定面试者的基础水平,保守估计,10个前端面试者,至少5个都死在闭包上. 可是为什么,闭包如此重要,还是有那么多人没有搞清楚呢?是因为大家不愿意学习吗?还真不是,

js执行上下文(由浅入深)

每一个函数都有自己的执行上下文EC(执行环境 execution context),并且每个执行上下文中都有它自己的变量对象VO(Variable object),用于存储执行上下文中的变量 .函数声明 .函数参数,这解释了js如何找到我们定义的函数和变量.并且函数是js中唯一一个能创建出作用域的,注意:for,if()else()不能创建作用域.我们通过以下几个例子说明为什么函数和变量的声明会被前置,为什么匿名函数表达式的不可以在外面调用. var a = 18; f1(); function