执行环境
JavaScript的解释器每次开始执行一个函数时,都会为那个函数创建一个执行环境(execution context),执行环境决定了变量的生命周期,以及哪一部分代码可以访问其中的变量。
JavaScript中主要存在的两种执行环境:
1.全局执行环境
JavaScript代码运行起来会首先进入该环境,通常被默认为window对象,所有的全局变量和函数都作为window对象的属性和方法存在。
2.函数执行环境
当函数被调用执行时,会进入当前函数中执行代码
3.Eval环境
执行eval()函数时创建。
当执行环境中的代码执行完毕之后,执行环境被销毁,其中的所有变量和函数也随之销毁。对于全局执行环境来说,当关闭网页或浏览器时,该环境被销毁。
JavaScript解释器是单线程的,在同一时间内只执行一个事件,所以JavaScript引擎会以堆栈的方式来处理它们。
当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈中。栈顶就是当前正在执行的上下文,完毕之后,栈将该函数的变量对象弹出,把控制权交给之前的执行环境变量对象。栈底永远都是全局上下文。
函数中,遇到return能直接终止可执行代码的执行,因此会直接将当前上下文弹出栈。
以上述执行环境分析给以下小例进行图解:
执行环境的属性:
变量对象(VO)、作用域链(scope chain)和this
- 变量对象(VO)
每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有参数,变量和函数都保存在这个对象中。
运行不属于任何函数的JavaScript代码对环境使用的就是全局对象,所有JavaScript函数都运行在自己独有的执行环境中,而且具有自己的调用对象
变量对象和活动对象(AO)
未进入执行阶段之前,变量对象(VO)中的属性都不能访问!但是进入执行阶段之后,变量对象(VO)转变为了活动对象(AO),里面的属性都能被访问了,然后开始进行执行阶段的操作。它们其实都是同一个对象,只是处于执行环境的不同生命周期。
- 作用域链(scope chain)
理解:根据在内部函数可以访问外部函数变量的这种机制,作用链式查找决定哪些数据能被内部函数访问,这就是作用域链。
作用域链由执行环境的变量对象组成,作用域链的最前端,始终是当前执行环境的变量对象。
当JavaScript代码需要查询变量x的值时,它首先查看该链的第一个对象(当前执行环境的变量对象),如果找不不到到,那么就继续查询第二个对象,依次类推。
var num = 10;
function f1(){
//如果f3 f2 f1 都没有定义 num, 则 f3 的 num 打印为全局定义的 10
var num = 20;
function f2(){
//如果f3 f2 都没有定义 num, 则 f3 的 num 打印为f1定义的 20
var num= 30;
function f3(){
//如果f3 没有定义 num, 则 f3 的 num 打印为f2定义的 30
var num= 40;
console.log(num);//40
}
f3();
}
f2();
}
f1();