Javascript有三种代码运行环境:Global, Function 和 Eval函数内部的执行环境。为了表示不同的运行环境,JavaScript中有一个执行上下文(Execution context,EC)的概念,也称执行环境。
当JavaScript代码执行的时候,执行流会进入到相应的执行环境,该执行环境被推入到一个环境栈中,函数执行之后,js引擎将其环境从栈中弹出,将控制权返回给之前的执行环境。
全局执行环境是默认的最外层的执行环境,在执行环境栈的最底部。
function outerFunc() {
console.log('In outer function EC!');
function innerFunc() {
console.log('In inner function EC!');
}
innerFunc();
}
outerFunc();
console.log('In global EC');
当执行innerFunc函数时,该环境栈是
Javascript执行环境中的重要属性
每个执行环境有三个重要的属性:变量对象(Variable Object)、作用域链(Scope Chain)、this指针。
活动对象(Activation object)和变量对象(Variable object)
只有全局环境的变量对象允许通过VO的属性名称间接访问;在函数执行上下文中,VO是不能直接访问的,此时由激活对象(Activation Object, 缩写为AO) 扮演VO的角色。激活对象是在进入函数上下文时刻被创建的,它通过函数的arguments属性初始化。
对于VO和AO的关系可以理解为,VO在不同的Execution Context中会有不同的表现:当在Global Execution Context中,可以直接使用VO;但是,在函数Execution Context中,AO就会被创建。
Javascript执行环境阶段
Javascript引擎创建执行环境分为两个阶段:创建阶段和激活/执行阶段。
创建阶段是Js引擎已经调用一个函数但函数还未开始执行的阶段,会创建激活对象或者变量对象、Scope chain、设置this指针的值。执行阶段Js引擎会设置变量的值、函数的引用,然后解释和执行代码。
function funA (a, b) {
var c = 3;
var d = 2;
d = function() {
return a - b;
}
}
funA(3, 2);
在A函数被调用但还未执行时,Js引擎为A函数创建了执行环境对象executonContextObj,如下:
funAExecutionContextObj = {
variableObject: {}, // All the variable, arguments and inner function details of the funA
scopechain: [], // List of all the scopes inside which the current function is
this // Value of this
}
对于创建激活对象或者变量对象,Js引擎具体做法是:
1.根据函数参数,创建并初始化arguments object;
2.扫描函数内部代码,查找函数声明(Function declaration)
- 对于所有找到的函数声明,将函数名和函数引用存入VO/AO中
- 如果VO/AO中已经有同名的函数,那么就进行覆盖
3.扫描函数内部代码,查找变量声明(Variable declaration)
- 找到所有的变量声明,将变量名存入VO/AO中,并初始化为’undefined’;
- 如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性
针对上面的例子,其中variableObject(变量对象)属性包含了函数内部的变量、函数声明以及arguments形参。具体为:
variableObject = {
arguments : {
0: a,
1: b,
length: 2
},
a: 3,
b: 2
c: undefined,
d: undefined then pointer to the function defintion of d
}
执行阶段,Js引擎扫描整个函数,更新变量值并执行代码。这个时候执行环境中的variableObject变为:
variableObject = {
arguments : {
0: a,
1: b,
length: 2
},
a: 3,
b: 2
c: 3,
d: undefined then pointer to the function defintion of d
}
对于变量对象VO,是有下面两种特殊情况的:
函数表达式(与函数声明相对)不包含在VO之中(此处有疑问)
没有使用var声明的变量不在VO中(这种变量是,”全局”的声明方式,只是给Global添加了一个属性)
(function(){
console.log(bar);
console.log(baz);
bar = 20;
console.log(window.bar);
console.log(bar);
function baz(){
console.log("baz");
}
})()
运行这段代码会得到”bar is not defined(…)”错误。当代码执行到”console.log(bar);”的时候,会去AO中查找”bar”。但是,根据前面的解释,函数中的”bar”并没有通过var关键字声明,所有不会被存放在AO中,也就有了这个错误。
注释掉”console.log(bar);”,再次运行代码,就不会报错。”bar”在”激活/代码执行阶段”被创建。
参考:
https://hackernoon.com/execution-context-in-javascript-319dd72e8e2c
http://www.cnblogs.com/wilber2013/p/4909430.html
https://github.com/mqyqingfeng/Blog/issues/8