JavaScript深入浅出-执行上下文

    • 全局
    • 函数
    • eval

每次调用函数都是重新生成一个作用域

概念-执行上下文

console.log('EC0'); 
function funcEC1(){ 
   console.log('EC1'); 
   var funcEC2=function(){ 
       console.log('EC2'); 
       var funcEC3=function(){ 
           console.log('EC3'); 
       } 
       funcEC3() 
   } 
   funcEC2() 
} 
funcEC1(); 
//EC0 EC1 EC2 EC3 

EC类似一个栈的结构,如图EC0是在Global下,当输出EC3的时候实际上当前是在EC3的这个作用域,等EC3调用结束完毕后退回到EC2-EC1-EC0

概念-变量对象

JS解释器如何找到我们定义的函数和变量?变量对象(variable Object )是一个抽象概念中的对象,它用于存储执行上下文中的:变量,函数声明,函数参数

执行上下文与变量对象

activeExecutionContext={ 
   VO:{ 
       data_var, 
       data_func_declaration, 
       data_func_arguments 
   } 
}; 
GlobalContentVO (VO===this===global) 
var a=10; 
function test(x){ 
   var b=20; 
   test(30); 
} 
VO(globalContext)={ 
   a:10, 
   test:<ref to function> 
}; 
VO(test functionContext){ 
   x:30, 
   b:20 
} 

全局VO即为global在定义test函数作用域时,首先会定义一个全局VO,存储全局变量,函数声明.当调用函数的时候会再定义一个函数VO并且存储函数中的变量和形参。

全局执行上下文(浏览器)

VO(globalContext)===[[global]]; 
[[global]]={ 
   Math:<..>, 
   String:<..>, 
   isNaN:function(){[native code]}, 
   ... 
   ... 
   ... 
   window:global // applied by browser(host) 
} 
GlobalContentVO (VO===this===global) 
String(10);//[[global]].String(10); 
window.a=10;//[[global]].window.a=10 
this.b=20;//[[global]].b=20 

浏览器中全局中的VO即等于global,全局下有个window指向global 所以能够window.window.window.Math;在全局中调用与声明间接的与VO去接触,在函数中去拿一个变量实际上也去函数VO拿一个变量

函数中的激活对象

VO(functionContext)===AO; 
AO={ 
   arguments:<Arg0> 
}; 
arguments={ 
   callee, 
   length, 
   properties-indexes 
} 

可以理解为 不同执行阶段下的一个不同东西…AO在函数调用的时候会有一个arguments,会放置在VO对象中,在arguments定义后AO就等于VO对象

变量初始化阶段

function test(a,b){ 
   var c=10; 
   function d(){}; 
   var e=function _e(){}; 
   (function x(){}); 
   b=20; 
} 
test(10) 

AO(test)={ 
   a:10, 
   b:undefined, 
   c:undefined, 
   d:<ref to func 'd'>, 
   e:undefined 
} 

对于函数来讲AO和VO是一个东西第一个阶段是变量初始化阶段.VO按照如下顺序填充

  • 函数参数(若未传入,初始化该参数值为undefined)
  • 函数声明(若发生命名冲突,会覆盖)
  • 变量声明(初始化变量值为undefined,若发生命名冲突,会忽略)

test(10)只传递了一个10,b未传递初始化为undefined,变量初始化为undefined和变量不存在是有区别的,如果是声明了,值是undefined,alert一下会弹出undefined,如果未声明,那么alert会报错 xx is not defined.为什么函数声明会被前置?在变量初始化阶段也就是在执行之前会把函数声明放到VO里面在处理变量声明中,c=10是一个赋值语句,目前只是一个初始化阶段,也就是仅仅声明一个var c并不会赋值.当声明有冲突的时候比如 b与b=20,虽然隐式声明了,但是因为存在b了也就是函数参数,所以给你忽略掉了这个隐式声明当函数声明与参数冲突了,后者会覆盖前者比如

function foo(x,y,z){ 
   function x(){}; 
   console.log(x); 
} 
foo(100);// function x(){} 函数声明会覆盖 


function foo(x,y,z){ 
   function func(){}; 
   var func; 
   console.log(func) 
foo(100)//function func(){} 变量声明 冲突会忽略 

function foo(x,y,z){ 
   function func(){}; 
   var func=1; 
   console.log(func); 第二阶段执行 
} 
foo(100)//1 

函数表达式不会影响VO,比如var e=function _e(){},这个_e不会记录到VO里,在第二阶段会赋值给e 这样就能在VO中拿到.

代码执行阶段

初始化阶段 
AO(test){ 
   a:10, 
   b:undefined, 
   c:undefined, 
   d:<ref to func 'd'> 
   e:undefined 
} 


function test(a,b){ 
   var c=10; 
   function d(){}; 
   var e=function _e(){}; 
   (function x(){}); 
   b=20; 
} 
test(10) 

代码执行阶段 

VO['c']=10; 
VO['e']=function _e(){}; 
VO['b']=20; 

AO(test)={ 
   a:10, 
   b:20, 
   c:10, 
   d:<ref to func 'd'>, 
   e:function _e(){} 
} 

在代码执行阶段就指向了一个 var c=10; 那么相当于执行了VO[‘c’]=10;当var e=function _e(){};相当于执行了VO[‘e’]=10;匿名函数是函数表达式 是不会影响VO 所以就忽略了(function x(){});如果是立即执行的,他又会有一个自己的执行上下文

测试

alert(x);//function 函数声明提前 

var x=10; 
alert(x);//10 执行阶段赋值并且覆盖了function 
x=20; //重新赋值 

function x(){} //声明提前 
alert(x);//20  

if(true){ 
   var a=1; 
}else{ 
   var b=true; 
} 

alert(a);//1 因为true 所以执行阶段赋值了 
alert(b);//undefined 未赋值仅定义所以是undefined 

词法环境

  • 环境记录 (VO) -形参 -函数声明 -变量 -..

环境记录初始化

词法环境例子

如图,那么分析下

全局初始化阶段

AO(global){ 
   x:undefined, 
   foo:<ref to func 'foo'>, 
   bar:undefined, 
   outer:null 
} 

全局执行阶段

AO(global)['x']=10; 
AO(global)['bar']= run func 'foo' 

foo初始化阶段

AO(foo){ 
   y:20, 
   bar:<ref to func 'bar'> 
   z:undefined 
   outer:global //为了理解写了个outer 意思就是上层作用域 
} 

foo执行阶段

AO(foo)['z']=30; 
AO(foo)return bar, go to global and set bar 

全局执行阶段-1

AO(global)['bar']=<ref to func 'bar'>; 
AO(global) bar(40)// run bar 

bar初始化阶段

AO(bar){ 
   q:40, 
   outer:foo 
} 

bar执行阶段

AO(bar) return x(find to outer...in global)+y(find to outer in foo)+z (find to outer in foo)+q(find to outer in bar) 

这样整个函数就执行完毕了

带名称的函数表达式

(function A(){ 
   A=1; 
   console.log(A)//function A 
})() 

上述说过 函数表达式不会出现在AO里面,那么这个例子

全局初始化阶段

AO(global){ 
   outer:null 
} 

全局执行阶段

AO(global) run IIFE 

A初始化阶段

AO(A){ 
   A:<ref to func 'A'> //无法被修改 
   outer:global 
} 

A执行阶段

AO(A)['A']=1 //因为无法被修改 所以A还是等于本身