详见:http://www.cnblogs.com/foodoi...html
JavaScript在执行一个“代码段”以前,即解析(预处理)阶段,会先进行一些“准备工做”,例如扫描JS中var定义的变量、函数名等,进而生成执行上下文。闭包
JS中的“代码段”分为三种:全局代码段、函数体代码段、eval代码段。(注:ES6以前,JS不存在“代码块”做用域的概念,即除了函数以外全部“{}”里的代码,都属于全局做用域)函数
全局代码段“准备工做”包括:ui
1.变量、函数表达式 —— 变量声明,默认赋值为undefined; 2.this —— 赋值; 3.函数声明 —— 赋值。
函数体代码段“准备工做”包括:this
1.变量、函数表达式 —— 变量声明,默认赋值为undefined; 2.this —— 赋值; 3.函数声明 —— 赋值; 4.参数 —— 赋值; 5.argument —— 赋值; 6.自由变量的取值做用域 —— 赋值。
evel()不推荐使用,因此再也不分析evel代码段。spa
至此,“执行上下文”的定义能够通俗化为 —— 在执行代码段以前(预处理阶段),把将要用到的全部变量都事先拿出来,有的直接赋值,有的先用undefined占个空,这些变量共同组成的词法环境,即为执行上下文环境。code
在执行js代码时,会有数不清的函数调用次数,会产生许多个上下文环境。这么多上下文环境该如何管理,以及如何销毁并释放内存呢?这就须要“执行上下文栈”来解释了。htm
经过上文咱们知道:预处理全局代码时,会产生一个执行上下文环境。每次调用函数的预处理时,都会产生一个执行上下文环境。其实,当这个函数调用完成时,它的执行上下文环境以及其中的数据就会被销毁,执行过程再从新回到全局上下文环境。同一时刻,处于活动状态的执行上下文环境只有一个。对象
实现这一压栈出栈过程的机制就是“执行上下文栈”。blog
执行上下文栈的压栈出栈过程实例代码:
var a = 10, //1.进入全局上下文环境 fn, bar = function(x) { var b = 5; fn(x+b); //3.进入fn函数上下文环境 }; fn = function(y) { var c = 5; console.log(y+c); } bar(10); //2.进入bar函数上下文环境
预处理时,首先建立全局上下文环境:
而后执行代码,全局上下文环境中的变量都被赋值:
当执行到调用bar函数时,跳转到bar函数内部,对其进行预处理,建立bar函数的执行上下文环境:
并将这个函数上下文环境压栈,设置为活动状态,开始执行bar函数体内代码:
当执行到调用fn函数时,跳转到bar函数内部,对其进行预处理,建立fn函数的执行上下文环境,并压栈,设置为活动状态,开始执行fn函数体内代码:
fn函数执行完毕后,这次调用fn所生成的上下文环境出栈,而且被销毁(已经用完了,就要及时销毁,释放内存),bar函数的执行上下文环境回到活动状态:
bar函数执行完毕后,调用bar函数所生成的上下文环境出栈,而且被销毁(已经用完了,就要及时销毁,释放内存),全局上下文环境回到活动状态:
全局代码执行完成,全局上下文环境出栈,而且销毁(已经用完了,就要及时销毁,释放内存),代码执行完毕。
以上是一段代码执行上下文环境的完整变化过程,但有一种状况的代码,其执行上下文环境并未按上述过程销毁,这就是接下来咱们的重点研究对象 —— 闭包!
要谈闭包,咱们还得先认识下做用域和自由变量,敬请期待... ...