执行环境以及作用域
按照小编的理解,应该呢,是先创建执行环境,然后通过执行环境来创建该环境的作用域链,毕竟,在实际中,函数有很多。
执行环境(execution context,为简单起见,有时也称为 “环境”)是 JavaScript 中最为重要的一个概念。
- 环境的定义:执行环境定义了 变量或函数 有权访问的 其它数据,决定了它们各自的行为。
- 环境有作用域分为哪些:分为 全局执行环境 和 局部执行环境两种,其中全局执行环境是最外围的一个执行环境,根据ECMAScript 实现所在的宿主环境不同,表示执行环境对象也不一样。在web浏览器中,全局执行环境是 window 对象。
- 环境的产生:每个函数在被调用时都会创建自己的 环境,在某个执行环境中,所有的代码执行完毕后,该环境就会被销毁,而全局执行环境则是整个程序退出被销毁,例如网页或则浏览器关闭。
- 环境的运作方式:环境产生之后,当执行流进入一个函数时,函数环境就会被推入一个环境栈中,而在函数执行完毕(环境被销毁),栈将其弹出,把控制权交给之前的执行环境。
- 每个执行环境都有一个与之关联的 变量对象(variable object),环境中定义的所有变量和函数都会保存在这个对象中,这个对象是不可见的,但解析器也可以访问到他们。
code:
var color ="red";function changeColor(){ var anotherColor = "blue"; function swapColors(){ //变量互换 //在这里可以访问到 annotherColor,color和 tempColor var tempColor = anotherColor; anotherColor = color; color = tempColor; } //这里可以访问到 anotherColor和color swapColors();}//这里可以访问到 colorchangeColor()
通过以上的代码可以看到,执行环境访问的变量数 由里向外 依次减少,这是为什么呢?
以上代码共涉及到 3个执行环境:全局环境、changeColor()的局部环境和swapColors()的局部环境。下图可清晰的展示其执行环境。
图 4-3 中矩形表示特定的 执行环境,其中内部环境可以通过作用链访问到所有的外包环境变量,但是,外部环境却不能访问内部环境中的任何变量和函数。这些环境之间的联系都是 线性的有次序的,而这种联系都是通过 作用链 来维持的。
每个环境都是向上搜索作用链,来查询变量和函数名,但是任何环境都不能通过向下搜索作用链而找查询变量和函数。
上例子中,对于这个 swapColors()函数而言,其作用链包含 三个变量对象:
window对象
changeColor()对象
swapColors()对象
swapColors()的局部函数开始的时候先在自己变量对象中搜索变量和函数名,如果搜不到再向上一级作用域链(changeColor())搜索。比如:anothenColor变量,在swapColors内部函数中时找不到这个值的,那么swapColors() 就会对 changeColor()说:你有没有anothenColor啊? changeColor说:我有,你拿去吧。由于changeColor()慷慨的赠送,那么swapColors()就获得了这个anothenColor变量。
changeColor()其作用域链包含两个变量对象:changeColor()对象和window对象。
作用域链:
- 作用域链的产生:当代码(函数)在一个执行环境中执行时,会创建一个由 变量对象(variable object)构成的一个 作用域(scope chain)。
- 作用域链的定义:保证对执行环境有权访问的所有变量和函数的有序访问,读起来有写拗口。
- 作用域链的运作方式:前面知道 环境的运作方式,小编也说过,作用域链是通过 环境 而产生的,那么作用域链的运作方式和环境几乎是大同小异,作用域链是分阶层的,通常,作用域链的最前端是当前执行环境的变量对象,如果这个环境是函数的话,那么函数的 活动对象(activetion object) 将作为变量对象。作用域中的下一个变量对象来自包含环境,而再下一个变量对象,来自下一个包含环境。这样一直延续到全局执行环境(全局执行环境对象始终都是作用域中的最后一个对象);
- 作用域链的作用:
当某个函数被调用的时候,会创建一个执行环境(execution context)以及相应的作用域链。然后,使用arguments和其它的命名参数的值来初始化函数的活动对象(activation object)。作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,...直至作用域为作用域链的终点的全局执行环境。
在函数执行过程中,为读写和写入的变量的值,就需要在作用域中查找变量。
function compare(va1,va2){ if(va1 < va2) { return -1;} else if(va1 > va2){ return 1;} else return 0;}var result = compare(5,10);
以上代码先定义了 compare() 函数,然后又在全局作用域中使用它。当调用 compare() 时,会创建一个 arguments、va1和va2的活动对象(activation object)。 全局执行环境的变量对象(包含result和compare)在compare()执行环境的作用域链中则处于第二位。图 7-1 展示了包含上述关系的 compare 函数的执行时的作用域链。
后台的每个执行环境都有一个表示变量的对象————变量对象。全局环境的变量对象始终存在,而像 compare() 函数这样的局部环境的变量对象,则只有在函数执行过程中才会存在。
在创建 compare()函数时,会预先创建一个包含全局变量的对象的作用域链,这个作用域链被保存在内部的[[Scope]]属性中。当调用 compare() 函数时,会为函数创建一个执行环境,然后通过复制函数的 [[Scope]] 属性中的对象构建起执行环境中的作用域链。此后,又有一个活动对象(在此作为变量对象使用)被创建并推入执行环境作用域的前端。对于这个例子中 compare()函数的执行环境而言,其作用域中包含两个变量对象:本地活动对象和全局变量对象。显然,作用域本质上是一个指向变量对象的指针列表,它只是引用但不实际包含变量对象。
注意:函数的参数也当做变量对待,因此其访问规则和执行环境中的变量相同。