作用域和作用域链

作者: 高少辉_骚辉 | 来源:发表于2017-05-21 13:17 被阅读17次

    一、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执行环境,继续刚才的作用域链(每个执行环境有一个指向自己作用域的指针)

    相关文章

      网友评论

        本文标题:作用域和作用域链

        本文链接:https://www.haomeiwen.com/subject/ozgmxxtx.html