var a = 3
function fn(){
    /*相当于先声明了a var a;a = undefined
    先在自身函数中找变量,再去全局找
    */
    console.log(a) //undefined
    var a = 4
}
fn()  //undefined
console.log(b) //undefined //变量提升
fn2() //可调用 函数提升
fn3() //变量提升,不可执行
var b = 3
function fn2(){
    console.log(‘fn2‘)
}
var fn3 = function(){
    console.log(‘fn3‘)
}
注意: 测试一:
function a(){}
var a
console.log(typeof a)  //function
测试二:
var c = 1  //=>①var c
function c(c){
    console.log(c)
    var c = 3  //无关
}
//=>②c = 1 =>c typeof number
c(2) //报错c is not a function
代码分类(位置)
全局执行上下文
在执行全局代码前将window确定为全局执行上下文
对全局数据进行预处理(收集数据)
开始执行全局代码
c = 3 //特殊:未执行语句之前,window中不包含此变量,需用var定义
console.log(a1,window.a1) //undefined
a2() //a2()
console.log(this) //window
var a1 = 2
function a2(){
    console.log(‘a2()‘)
}
console.log(a1) //2
函数执行上下文
在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中)
对局部数据进行预处理
开始执行函数体代码
function f1(a1){
    console.log(a1)
    console.log(a2)
    a3()
    console.log(this)
    console.log(arguments)
    var a2 = 3
    function a3(){
        console.log(‘a3()‘)
    }
}
f1(2,3) //2,undefined,a3(),window,2 3
//调用一次函数产生一次执行上下文栈n+1(window)
//1、进入window全局执行上下文
var a = 10
var bar = function(x){
    var b = 5
    //2、进入foo执行上下文
    foo(x + b)
}
var foo = function(y){
    var c = 5
    console.log(a + c + y)
}
//3、进入bar函数执行上下文
bar(10)  //30


console.log(‘global begin:‘ + i)
var i = 1
foo(1)
function foo(i){
    if(i == 4){
        return
    }
    console.log(‘foo() begin:‘ + i)
    foo(i + 1)//递归调用:在函数内部调用自己
    console.log(‘foo() end:‘ + i)
}
console.log(‘global end:‘ + i)
/*函数依次输出:
global begin:undefined
foo() begin:1
foo() begin:2
foo() begin:3
foo() end:3
foo() end:2
foo() end:1
global end:1
整个过程中产生了五个执行上下文
*/

原文:https://www.cnblogs.com/PHY01/p/15009407.html