• 关于闭包,前后文,作用域链


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


    例子:   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
  • 相关阅读:
    缓存一致性问题
    缓存雪崩
    mysql Replication机制
    数据库水平切分、拆库拆表
    mysql分表和分区实际应用简介
    mysql中间件
    mysql基础知识
    go语言redis使用(redigo)
    nginx location配置与rewrite配置
    PDO驱动使用
  • 原文地址:https://www.cnblogs.com/leolovexx/p/5696274.html
Copyright © 2020-2023  润新知