先从计算机角度说一下内存:内存,包括三个部分:只读存储器(ROM)、随机存储器(RAM)和高速缓冲存储器(Cache)。javascript
而其中,高速缓冲存储器(Cache)又分为三种:一级缓存(L1 Cache)、二级缓存(L2 Cache)、三级缓存(L3 Cache)。java
当CPU须要数据的时候,就会先找缓存,由于缓存最快,缓存找不到,才去找慢一点的内存,而后找到后继续将数据放入缓存,下次还要的时候,仍是先找缓存。git
简单来讲,缓存中的数据只是内存的一部分,可是读写速度最快,因此CPU先找它。github
扯远了,回过头来。算法
再解释一下常说的“堆内存”、“栈内存”,“栈内存”也叫作“堆栈内存”,不是两个一块儿的总称,实际上就是栈内存,因此我这里就分别说“堆内存”和“栈内存”。缓存
这里面就有“堆”和“栈”两个概念了。网络
先说说栈,它是放在是在于一级缓存中的,数据被调用的时候放入存储空间中,而后被调用完成时候,就会被马上释放。函数
再说堆,它是放在二级缓存中的,它的生命周期由虚拟机的垃圾回收算法来决定。因此调用这些对象的速度要相对来得低一些。this
举个很简单的例子来解释“堆”和“栈”这两个概念:spa
栈,有点像汉诺塔那样的套圈圈,一圈一圈地放上去,前面放的都被压在了底部,后面的就压在上面,一层一层叠罗汉,可是取出来的时候,就是后面放上去的先取出来,越早放进去的越后取出来,简单来讲,就是迟来先上岸。
堆,就像是一堆东西那些,就好像你杂乱的房间,一堆杂物,你想找东西,翻来翻去,找到了,能够拿走,有些东西,你不拿走,放在那里,其实就是垃圾。
通常来讲,javascript中的数据类型分为基本数据类型和引用数据类型,而基本数据类型中的变量名和变量直接存放在栈内存中,而引用数据类型的变量值其实是存放的一个地址指针,因此它的变量名和变量值也是存放在栈内存中,而地址指向的实际内容,则是存放在堆内存中。
好了,开始说一下执行上下文了。
有些人会混淆执行上下文和做用域的概念,后面的文章我会说到做用域和做用域链,如今先说执行上下文。
从执行上下文的生命周期来讲,包括三个部分:
一、建立阶段;二、执行阶段;三、执行完毕阶段。
1、建立阶段
执行上下文是在函数被调用的时候才建立,主要有三个内容:
一、建立变量对象;二、初始化做用域链;三、肯定this的指向。
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那一层的时候,还没执行完这个函数,又调用其它函数,其它的执行上下文又接着压上去了。
固然,道理都是同样的。