2

关于javascript中的从堆栈内存到执行上下文

我GitHub上的菜鸟仓库地址: 点击跳转查看其他相关文章
文章在我的博客上的地址: 点击跳转

        先从计算机角度说一下内存:内存,包括三个部分:只读存储器(ROM)、随机存储器(RAM)和高速缓冲存储器(Cache)。

        而其中,高速缓冲存储器(Cache)又分为三种:一级缓存(L1 Cache)、二级缓存(L2 Cache)、三级缓存(L3 Cache)。

        当CPU需要数据的时候,就会先找缓存,因为缓存最快,缓存找不到,才去找慢一点的内存,然后找到后继续将数据放入缓存,下次还要的时候,还是先找缓存。

        简单来说,缓存中的数据只是内存的一部分,但是读写速度最快,所以CPU先找它。

        扯远了,回过头来。

        再解释一下常说的“堆内存”、“栈内存”,“栈内存”也叫做“堆栈内存”,不是两个一起的总称,实际上就是栈内存,所以我这里就分别说“堆内存”和“栈内存”。

        这里面就有“堆”和“栈”两个概念了。

        先说说栈,它是放在是在于一级缓存中的,数据被调用的时候放入存储空间中,然后被调用完成时候,就会被立刻释放。

        再说堆,它是放在二级缓存中的,它的生命周期由虚拟机的垃圾回收算法来决定。所以调用这些对象的速度要相对来得低一些。

        举个很简单的例子来解释“堆”和“栈”这两个概念:

        栈,有点像汉诺塔那样的套圈圈,一圈一圈地放上去,前面放的都被压在了底部,后面的就压在上面,一层一层叠罗汉,但是取出来的时候,就是后面放上去的先取出来,越早放进去的越后取出来,简单来说,就是迟来先上岸。

        堆,就像是一堆东西那些,就好像你杂乱的房间,一堆杂物,你想找东西,翻来翻去,找到了,可以拿走,有些东西,你不拿走,放在那里,其实就是垃圾。

        一般来说,javascript中的数据类型分为基本数据类型和引用数据类型,而基本数据类型中的变量名和变量直接存放在栈内存中,而引用数据类型的变量值实际上是存放的一个地址指针,所以它的变量名和变量值也是存放在栈内存中,而地址指向的实际内容,则是存放在堆内存中。

        好了,开始说一下执行上下文了。

        有些人会混淆执行上下文和作用域的概念,后面的文章我会说到作用域和作用域链,现在先说执行上下文。

        从执行上下文的生命周期来说,包括三个部分:

        1、创建阶段;2、执行阶段;3、执行完毕阶段。

        一、创建阶段

        执行上下文是在函数被调用的时候才创建,主要有三个内容:

        1、创建变量对象;2、初始化作用域链;3、确定this的指向。

        二、执行阶段

        发生在函数代码执行阶段,主要有三个内容:

        1、变量赋值;2、函数引用;3、执行其他代码。

        三、执行完毕阶段

        主要内容:执行完毕后跳出执行上下文栈,等待被回收。

        关于创建阶段和执行阶段的具体内容,可能大家会有疑惑,里面的具体内容后面文章会慢慢细说。这里单纯说说执行上下文栈。

        举个简单例子,函数里面也会有嵌套函数的情况,就像这样:

//函数father
function father(age){
    var me = age + 20;
    //函数son
    function son(age){
        return age;
    }
    return son(me);
}
father(33);

        既然执行上下文是函数被调用的时候创建的,那么上面这个father函数被调用之后,然后son也被调用了,那它们的执行上下文是什么关系呢?

        在这里,Javascript会利用执行上下文栈(Execution context stack,ECS)来管理执行上下文。

        回想一下前面栈内存的概念就很容易理解。

        当调用某个函数的时候,就会创建执行上下文,并压入执行上下文栈中,当执行完毕的时候,就会从执行上下文栈中跳出,等待被回收。像上面这种函数内嵌套函数的情形,调用father函数的时候,father创建的执行上下文压入栈中,然后开始执行father的函数体内代码,因为father函数还没执行完毕,所以调用son函数时候会将son创建的执行上下文压入栈中,当son执行完毕,就会跳出,然后father执行完毕,继续跳出。这就完成了整个father的执行上下文周期。

        还是那句,迟来先上岸的感觉。就好像下面的图这样(图片引用自网络),下面就是一个执行上下文栈,最底层肯定是全局了,然后只要函数没执行完毕继续在函数内调用其它函数的话,其它函数的执行上下文就会接着压上去,最后执行完毕,压在最上面的上下文先清出,然后其它执行上下文又变成最上面的了,然后执行完毕,继续清出,就和图那样了。

        实际情况可能不是图的那样简单,可能清出到EC2那一层的时候,还没执行完这个函数,又调用其它函数,其它的执行上下文又接着压上去了。

        当然,道理都是一样的。
es1.gif


ershing
325 声望26 粉丝