• 详解JavaScript作用域及作用域链


      一、js没有块级作用域

      在强类型语言中都有块级作用域,例如,有如下C#代码

    for(int i = 0; i < 10; i++)
    {
          //do something 
    }
    Console.WriteLine(i);   

      这样的代码是无法通过编译的,因为循环变量i是一个局部变量,它的作用域范围只在for循环的大括号{}之内,出了这个大括号就不是i的作用域了,所以Console.WriteLine(i); 这条语句就不能编译通过了。

      但如果这样类似的代码是js写的就另当别论了。比如:

    for(var i = 0; i < 10; i++){
         //do something;  
    }
    console.log(i);//输出10

      这个代码是没有问题的,运行结果是10 。这说明了js没有传统意义上的块级作用域。

      但这不代表js没有局部作用域。js的作用域只有全局作用域和函数作用域,js也只有函数能产生局部作用域。

      一、全局作用域

      只要是在一对<script>标签之内直接声明的对象,它的作用域就是全局的。

      如果一个对象是在function内部声明的,那么它的作用域就是局部的,函数内部的作用域。

       比如:

    <script>
        var a = 123; 
        console.log(a); //输出123
        function f1(){ 
           console.log(a);
        } 
        f1();//输出123
    </script>

      此例中,a就是直接声明在了一对<script>标签内的,不是声明在某一个函数之内的,那么它是个全局作用域,所以不管是在f1函数内部,还是在外部,都可以访问到变量a

      二、函数作用域

      把上边的例子稍微改改

    <script>
        var a = 123; 
        console.log(a); //输出123
        function f1(){ 
           var num = 10;
           console.log(a);
        } 
        f1();//输出123
        console.log(num); // 报错,无法访问到num
    </script>

      这个代码会报错,因为num是在f1函数内部声明的,它的作用域仅限于f1函数声明的内部,也就是f1那个大括号{}内部,所以在函数声明的外部访问num肯定是不可以的。

      我们可能会看到一个词,说js中的作用域是:词法作用域。那么怎么理解这个所谓的“词法作用域”呢?

      看这个例子

    <script>
        var a = 123; 
        console.log(a); //输出123
        function f1(){ 
           console.log(a);
        } 
        function f2(){ 
           var a = 456;
           f1(a);
        } 
        f2();//输出123
    </script>

      这个例子就很值得思考了。

      1.有一个全局作用域的变量a

      2.f1函数调用了a

      3.f2函数先声明了一个局部变量a,并且与全局变量同名

      4.f2函数声明了这个同名的局部变量a之后,又调用了f1函数

      这个程序执行完之后的结果是 : 123 , 而不是456。原因是js中不存在块级作用域,只存在函数作用域。

      而函数作用域又被称之为词法作用域,说白了就是:你的函数时在哪儿声明的,那它就属于哪儿,也就意味着它只能访问到这个区间的成员。

      在这个例子中,f1函数是声明在全局作用域下的,那么它就属于全局作用域,也就意味着它(函数体内部)只能访问到全局作用域下声明的对象。显然,当前全局作用域下的a是等于123的,而不是f2内部声明的那个等于456的a,所以,不管是在何时调用f1这个函数,它访问到的a必然是那个值等于123的全局变量a。

      那究竟该如何清晰的描述出对象的作用域范围?这就要使用一个叫做“作用域链”的东西了。

      三、作用域链

      看这个例子就明白什么是作用域链了。

    <script>
        var num = 1;
    
        function f1(){
            var num = 2;
            function f2(){
                var num = 3;
                function f3(){
                    console.log(num);
                }
                f3();
            }
            f2();
        }
    
        f1(); //输出 3
        // f2();//报错,
        // f3();//报错
    
    </script>

      这个例子就是函数嵌套声明,并且每个函数内部还都又声明了一个跟全局变量num同名的局部变量。相信通过刚才的解释,大家已经能清晰的得出程序执行结果了。

      简单说明一下:

      1.在全局作用域下,不能直接调用f2和f3函数,因为它们都是在f1内部嵌套声明的,它们都是局部作用域声明的对象,所以不能在全局作用域中访问。

      2.在局部作用域中,如果出现于更高级的作用域同名的对象,那么优先访问本作用域范围之内的对象

      3.如果在本作用域之内找不到,那么就往“上一级”作用域中去找,知道全局作用域,如果还没有就报错。

      那么这里所谓的“上一级”作用域,可以使用作用域链清晰的描绘出来。这个例子的作用域链我们可以用图画出来。

           

      首先这里的全局作用域我们称之为 0 级作用域,f1函数时在全局作用域下声明的,所以f1自己形成一个小的作用域,称之为 1 级。以此类推。

      当我们在外部调用f2和f3是不可能的。因为从图上我们清晰的看到,在0级作用域的横线上根本没有声明一个叫f2或者f3的对象。

      当我们在全局(0级)作用域下,调用f1函数,然后逐级的调用到f3的时候,f3内部有一条console.log(num)。此时,程序会沿着我们画的线,现在f3的作用域,也就是3级作用下找有没有声明一个叫num的对象,如果有就直接拿来打印,如果没有,则沿着这个作用域链往上递推,首先推到离他最近的上一级作用域,也就是f2的作用域,它是2级作用域,这里声明了一个叫做num的变量,并且赋了初值为3,于是,这条打印语句,就立刻把这个=3的num拿来使用,而不再理会更高级的作用域了。因为它已经找到了一个可以访问到的num。

      我们把这个图描绘的作用域之间的关系,就形象的称之为作用域链。

      在作用域链中搜索对象,是只能逐级往上查找的,而不能往低级的作用域中查找。

  • 相关阅读:
    完全图解scrollLeft,scrollWidth,clientWidth,offsetWidth 获取相对途径,滚动图片
    Input的size,width,maxlength属性
    Linux,VI命令详解
    Javascript 第十章
    Javascript 第七章
    IE css hack
    Javascript 第九章
    js中document.documentElement 和document.body 以及其属性
    关于xmlhttp.status == 0的问题
    Javascript 第八章
  • 原文地址:https://www.cnblogs.com/ldq678/p/9703368.html
Copyright © 2020-2023  润新知