作用域
作用域是指程序源代码中定义变量的区域。
作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。
JavaScript 采用词法作用域(lexical scoping),也就是静态作用域。
想了解更多关于作用域的问题推荐阅读《你不知道的JavaScript上卷》第一章(或第一部分),从编译原理的角度说明什么是作用域。概括的说作用域就是一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量。
词法作用域
在你不知道的javascript上卷中是这样定义的:词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变(大部分情况下是这样的)。
在JS中词法作用域的规则:
- 函数允许访问函数外部的数据
- 整个代码结构中只有函数可以限定作用域
- 作用规则首先使用提升规则分析
- 如果当前作用域中有了名字了,就不考虑外面的名字
栗1:
1 | var a = 2; |
栗2:
1 | function foo() { |
作用域链
只有函数才能制造作用域结构,那么只要是代码,至少有一个作用域,即全局作用域。
凡是代码中有函数,那么这个函数就构成另一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域,那么将这样的所有作用域列出来,可以有一个结构:函数内指向函数外的链式结构。
以上面栗2:
1 | function foo() { |
- 全局作用域,有三个标识符:foo、bar、a
- bar 所创建的作用域,其中有一个标识符:a
作用域是由代码写在哪里决定的,并且是逐级包含的。
我们用级链来表示一下以上代码块:
从图中我们可以看到:函数foo和bar以及变量 a=2, 在1级链,而bar下又可以展开2级链,2级链上有a=3。程序bar()调用进入中间2级链,而bar中又调用了foo函数,此时程序进入foo的作用域链,按照向上查找的原则,程序会从这一条链向上查找,首先2级链没有a,向上到达1级链,刚好1级链上有a=2,所以就直接使用2,程序最后的结果就是打印2。
所以无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定。
实践
1 | var scope = "global scope"; |