作用域和作用域链

一、JS的作用域

通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
在JS里面作用域分为全局作用域,局部作用域

全局作用域

变量的定义没有在任何函数内部,就都是全局变量

局部作用域

变量定义在函数的内部,那么在函数的外部则没有权限访问这个变量,对于全局环境来说,这个变量是隐藏的,且当局部变量和全局变量同名时,在局部变量的作用域内,局部变量具有更高的优先级

提前声明

var scope = "global";//全局变量的定义,没有在任何函数内部
function f(){
  console.log( scope ); //=>undefined 为局部变量,虽然代码上看上去还为定义,但是有个提前声明
  var scope = "local";//在函数内部定义,为局部变量
  console.log( scope ); //=>"local"
}

这段代码说明了,JavaScript函数里声明的所有变量(但不包含赋值,赋值位置不变)被“提前”到这个函数的最顶部。

上面的等价代码

var scope = "global";
function f(){
  var scope;            //定义了一个变量,但是为赋值
  console.log( scope ); //=>undefined
  scope = "local";
  console.log( scope ); //=>"local"
}

二、作用域链

何为作用域链呢?我想很多人听到这个时候,也挺迷茫的。因为明白作用域的都知道,作用域是限定这个名字的可用性的代码范围。那么为什么还需要来一个作用域链呢?其实是这样:作用域是实现的结果,而作用域链则是解释器是如何来完成作用域的方法

当某个函数第一次被调用时会创建一个执行环境(EC)以及相应的作用域链,并把作用域链赋值给一个特殊的内部属性[[Scope]]。然后使用 this, arguments 和其他的命名参数值来初始化函数的活动对象(AO),但是在函数的作用域链中,外部函数的活动对象始终处于第二位,以此类推。作用域的终点是全局执行环境。

作用域链图解
从上图就可以看出:

  • 当没有调用任何函数的时候,只有一个全局作用域
  • 当嵌套一个函数时,把局部作用域压入[[Scope]]的首部(0的位置,全局的编成1),而访问变量时,查找[[Scope]]这个对象的顺序是按照升序,也就是说先访问查找局部的表中是否有定义这个变量,如果没有在查找上一级,这也就可以理解为什么局部的优先级比全局的高
  • 当多层定义时,以递归的形式进行上一步
  • 当然有一个特例,就是使用with关键字,使用with时with里面的作用域直接提到作用域的顶端[[Scope]]的0的位置。
  • 当非嵌套定义,但是在一个函数里面调用了另一个函数时,这时的作用域链,将更加复杂,比如:
function fn1(){
  var a;
  fn2();
}
function fn2(){
  var b;
}

分析可知,首先新建一个fn1的执行环境,然后通过赋值函数的[[Scope]]属性中的对象构建起执行环境的作用域链,然后在fn1中调用了fn2但是fn2的定义并不是在fn1中而是全局,所以也将新建一个作用域链,它和fn1的作用域链是相互独立的,当fn2执行完以后,此作用域链删除,fn2执行环境删除,回到fn1执行环境,继续刚才的作用域链(每个执行环境有一个指向自己作用域的指针)

posted @ 2016-01-27 18:43  gw_iron  阅读(197)  评论(0编辑  收藏  举报