执行环境及作用域链是JavaScript中一个很重要的概念。本文志在整理清楚执行环境具体是什么,以及函数执行时的作用域链是如何创建的。
执行环境:
执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。
- 变量对象 每个执行环境都有一个与之关联的变量对象(variable object)。环境中定义的所有变量和函数都保存在这个对象中,这个对象无法访问,解析器在后台使用它。
- 执行环境的分类 执行环境一般有全局执行环境以及函数的执行环境。
- 全局执行环境是最外围的一个执行环境,在 Web 浏览器中,全局执行环境被认为是 window 对象;
- 每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。
作用域链
首先说一下为什么有作用域链,作用域链保证了对执行环境有权访问的所有变量和函数的有序访问。
当代码在一个环境中执行时,会创建变量对象的一个作用域链,作用域链的前端始终都是当前执行的代码所在环境的变量对象,下一个变量来自包含(外部)环境,再下一个变量对象来自下一个包含环境...一直延续到全局执行环境(作用域链的最后一个对象)。
所以函数的局部环境不仅有权访问函数作用域的变量,而且有权访问其包含环境,直到全局环境。(这也是闭包的产生原因)反过来访问却不行。
作用域链的一些细节
当某个函数被调用时,会先初始化函数的活动对象(activation object),这个对象使用 arguments 和其他命名参数的值来初始化。前面我们说过,当代码在一个环境中执行时,会创建变量对象的一个作用域链,作用域链的前端始终都是当前执行的代码所在环境的变量对象。实际上,当环境为函数时,会将其活动对象作为变量对象,该变量对象只在函数执行的过程中存在。函数作用域链的上一层是其包含环境的变量对象。
看下面的代码(来自JS红宝书):
function compare(value1, value2){
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
}
var result = compare(5, 10);
以上代码先定义了 compare()函数,然后又在全局作用域中调用了它。当调用 compare()时,会创建一个包含 arguments、value1 和 value2 的活动对象。全局执行环境的变量对象(包含 result 和 compare)在compare()执行环境的作用域链中则处于第二位。
那么作用域链具体是如何联系起来的呢?
- 在创建 compare 函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在内部的[[Scope]]属性中;
- 当调用 compare 函数时,会为函数创建一个执行环境,然后通过复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链;
- 之后,又有一个活动对象(在此作为变量对象使用)被创建并被推入执行环境作用域链的前端。
对compare()函数的执行环境而言,其作用域链中包含两个变量对象:本地活动对象和全局变量对象。显然,作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。
看以下JS高级程序设计书中的例子再体会一下:
function createComparisonFunction(propertyName) {
return function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
};
}
作用域链图解:
块级作用域(拓展)
JavaScript没有块级作用域,由花括号封闭起来的块在其他类C语言中都有自己的作用域,比如下面的代码:
if(true){
var a = 'test';
}
alert(a)//弹出test
这个代码的执行结果,依然会弹出变量a的值test。原因在于JS没有块级作用域,if语句内变量声明实际上是在全局环境中声明的,所以alert可以访问到。
以上验证了JS没有块级作用域,但是函数在执行的时候,作用域链是“从里往外”找,而不能“从外往里”找。所以,我们实际可以用函数来模仿块级作用域,通常使用匿名函数来模仿块级作用域:
(function(){
//这里是块级作用域
})();
以上立即执行的匿名函数中的变量与外部是完全隔离的,成功产生了一个块级作用域。
结束
以上关于JS中的执行环境与作用域链的学习到这里为止,记录下来作为自己的学习笔记,如果能帮到大家更好~