执行环境、作用域、作用域链

执行环境(execution context)

执行环境定义了变量或者函数有权访问的其他数据,决定了他们各自的行为。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的变量和函数都保存在这个对象中。

全局执行环境

全局执行环境是最外围的一个执行环境。在web浏览器中,全局执行环境被认为是window对象。某个执行环境中的所有代码被执行完毕之后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁。(全局执行环境直到应用程序退出----例如关闭网页或者浏览器时才会被销毁)

执行环境

每个函数都有自己的执行环境。当执行流进入到一个函数时,函数的环境会被推入到一个环境栈中。而在这个函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
执行环境始终是this关键字的值,它是拥有当前所执行代码的对象的引用,函数的每次调用都会创建一个新的执行环境。
ps:代码在下面的例子中


作用域链

当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。
作用域链的用途,是保证对执行环境有权访问的所有变量或者函数有序访问。
(1) 作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中是不存在的)。
(2) 作用域链中的下一个变量对象来自包含(外部)环境,而在下一个变量对象则来自下一个包含环境。这样一直延续到全局执行环境。
(3) 全局执行环境的变量对象始终都是作用域链中的最后一个对象。
(4) 标识符解析是沿着作用域链一级一级的搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级的向后回溯,直至找到标识符为止。如果找不到标识符,通常会导致错误发生。
(5) 在局部作用域中定义的变量可以在局部环境中与全局变量互换使用。

例子

  function fn1(){
    var a = 1;
    return function fn2(){
        return a;
    }
  }
  var b = fn1()
  var c = b() //1
执行环境、变量对象、作用域链之间的关系

上图体现执行环境、作用域链和变量对象(活动对象)之间的关系。调用fn1返回函数fn2 ,fn2内部访问了fn1中定义的a,当fn2调用时返回a=1,但是fn2中没有定义1,所以会顺着作用域链向上找,直至找到a,没有则报错。

因为这里应用了闭包,当fn1执行结束,fn1的执行环境会销毁,但是由于a被fn2访问,所以fn1作用域链会断开,但是变量对象保留,供fn2访问。

推荐阅读更多精彩内容