首先明确几个概念:

    EC:函数执行环境(或执行上下文),Execution Context.
    EC分为两个阶段,创建执行上下文和执行代码。
    1.进入上下文阶段:发生在函数调用时,但是在执行具体代码之前(比如,对函数参数进行具体化之前)
    2.执行代码阶段:变量赋值,函数引用,执行其他代码。

    1. EC={
    2. VO:{/* 函数中的arguments对象, 参数, 内部的变量以及函数声明 */},
    3. this:{},
    4. Scope:{ /* VO以及所有父执行上下文中的VO */}
    5. }

    ECS:执行环境栈,Execution Context Stack。ECStack管理EC的压栈和出栈

    VO:变量对象,Variable Object
    变量对象(Variable object)是说JS的执行上下文中都有个对象用来存放执行上下文中可被访问但是不能被delete的函数标示符、形参、变量声明等。

    AO:活动对象,Active Object:在函数的执行上下文中,VO是不能直接访问的。它主要扮演被称作活跃对象(activation object)(简称:AO)的角色。就是被激活的VO

    scope chain:作用域链:可以把它理解成包含自身变量对象和上级变量对象的列表,通过 [[Scope]] 属性查找上级变量

    当javascript代码文件被浏览器载入后,默认最先进入的是一个全局的执行上下文。当在全局上下文中调用执行一个函数时,程序流就进入该被调用函数内,此时引擎就会为该函数创建一个新的执行上下文,并且将其压入到执行上下文堆栈的顶部(压栈,进栈)。浏览器总是执行当前在堆栈顶部的上下文,一旦执行完毕,该上下文就会从堆栈顶部被弹出(出栈或退栈),然后,进入其下的上下文执行代码。这样,堆栈中的上下文就会被依次执行并且弹出堆栈,直到回到全局的上下文。

    当执行 JS 代码时,会产生三种执行上下文:
    全局执行上下文
    函数执行上下文
    eval 执行上下文

    每个执行上下文(EC)中都有三个重要的属性:
    变量对象(VO),包含变量、函数声明和函数的形参,该属性只能在全局上下文中访问。
    作用域链JS 采用词法作用域,也就是说变量的作用域是在定义时就决定了)。
    this

    变量提升:通常提升的解释是说将声明的代码移动到了顶部,这其实没有什么错误,便于大家理解。但是更准确的解释应该是:在生成执行上下文时,会有两个阶段。第一个阶段是创建的阶段(具体步骤是创建 VO),JS 解释器会找出需要提升的变量和函数,并且给他们提前在内存中开辟好空间,函数的话会将整个函数存入内存中,变量只声明并且赋值为 undefined,所以在第二个阶段,也就是代码执行阶段,我们可以直接提前使用。
    在提升的过程中,相同的函数会覆盖上一个函数,并且函数优先于变量提升

    更多参考:https://segmentfault.com/a/1190