美文网首页
JavaScript词法作用域

JavaScript词法作用域

作者: viviChen | 来源:发表于2019-10-15 15:31 被阅读0次

    作用域

    章节直通车:

    作用域的工作机制分为两种,一种是众所周知的词法作用域,另一个是不太熟悉的动态作用域。下面都具体讲解一下两者的不同之处。

    首先具体聊聊词法作用域,这也是大部分语言(包括JavaScript)的一种作用域机制。

    词法作用域

    词法作用域是在词法分析的时被定义的作用域。在上一篇文章中我们讨论了JavaScript引擎、编译器和作用域之间的关系,知道了词法作用域是一组关于 引擎如何查询变量和他在何处能够找到变量的规则

    词法作用域是在代码被编辑的时候定义的(假定不使用特定代码作弊的前提下,后面章节会涉及到如何作弊)。

    比如下面这一段代码:

    function foo(a) {
    
        var b = a * 2;
    
        function bar(c) {
            console.log( a, b, c );
        }
    
        bar(b * 3);
    }
    
    foo( 2 ); // 2 4 12
    

    KCNz36.png

    根据代码和上图所画出的作用域气泡,我们可以很清楚的知道变量分别是属于哪个作用域,这是在代码正在编辑的时候我们就可以明确出来。

    上面的函数存在嵌套关系,比如foo函数内又有bar函数,我们可以通过表格更清楚地展示他们之间的所属关系:

    气泡id 所属作用域 直属变量
    1 全局作用域 foo
    2 foo的函数作用域 a, b, bar
    3 bar的函数作用域 c

    我们只从bar(b*3)分析(上面的我们获取到了a为2,b为4),这里首先给c赋值为12,然后需要打印console.log( a, b, c );

    我们知道bar作用域只有c一个变量,那a和b是怎么查询到的呢?

    这就关乎到词法作用域的作用机制,当你在当前作用域找不到需要的变量时,他会向上去查找,直到全局作用域。

    比如我们现在要获取a和b的值,当前bar的函数作用域不存在,就会往上找到foo的函数作用域(因为bar被包含在foo的函数作用域内),在foo的作用域同时存在a、b的值,所以就查询到了。

    我们将这个函数稍微变一下:

    var b = 1;
    
    function foo(a) {
      console.log(a);
      console.log(b);
      console.log(c);
    }
    
    foo( 2 );
    
    气泡id 所属作用域 直属变量
    1 全局作用域 foo, b
    2 foo的函数作用域 a

    函数foo需要分别打印a,b,c三个变量的值,a属于foo函数作用域内的变量可以直接找到,打印a为2;b需要往上找到全局作用域,打印b为1;c往上找,可是全局作用域内不存在c这个变量,所以引擎会抛出错误VM127:3 Uncaught ReferenceError: c is not defined

    可能有人要问了为什么没有直接给全局变量加入c这个变量,打印为undefined呢?

    这就是上一章的问题了,只有LHS引用才会创建新的变量,这里属于RHS引用,如果查询不到就会抛出异常。

    动态作用域

    动态作用域和词法作用域的机制完全不同,但是我们仍可以在JavaScript中找到相似的机制,那就是this。

    在上一段中我们反复强调了 —— 词法作用域是根据编写时的顺序确定的,而动态作用域是根据调用时的位置所确定的。

    我们来分析下面一段代码:

    function foo() {
        console.log( a ); // 2
    }
    
    function bar() {
        var a = 3;
        foo();
    }
    
    var a = 2;
    
    bar();
    

    根据词法作用域,这里的a的值获取的是全局作用域中定义的a,值为2;

    而在动态作用域中,由于foo是在bar函数中被调用的,这里定义了一个a为3,所以foo中的a指的就是调用点处的a,输出为3。如果调用的地方不存在a的定义,就会到全局作用域中查询 —— 这跟this调用机制简直一模一样!!!

    总结

    1. 作用域分为词法作用域和动态作用域,JavaScript作用域属于词法作用域。

    2. 词法作用域是根据代码编写时所确认的,动态作用域是根据代码被调用时的调用点所确定的。

    3. 词法作用域对于变量查询是由内而外的,直到最外层的全局作用域,对于RHS查询,如果查询到最外层全局作用域都没有获取到值,会抛出引用异常错误,对于LHS查询,如果查询到最外层没有获取到,就会在全局作用域中创建当前变量(非严格模式下)。

    相关文章

      网友评论

          本文标题:JavaScript词法作用域

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