该系列是《你不知道的JavaScript(上)》的读书笔记。记录成文字,加深学习印象。

什么是词法作用域

词法作用域就是定义在词法阶段的作用域。是不是很难理解呢、换句话说就是,词法作用域意味着作用域由书写代码时函数的位置来决定的。如果还不能理解的话,先以一个简单的例子来说明:

1
2
3
4
5
6
7
8
9
function fn1(a) {
var b = a * 2;

function fn2(c) {
console.log(a, b, c);
}
fn2(b * 3);
}
fn1(2); // 2, 4 ,12

上面这个例子包含着三个作用域

  • 全局作用域
  • fn1的作用域
  • fn2的作用域
    根据它们定义的位置,这三个作用域应该是逐级包含的关系。

如果上面这个例子可以理解的话,那么再来看一个相对复杂一点的例子:

1
2
3
4
5
6
7
8
9
10
function foo() {
console.log(a);
}

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

最终的输出结果是2。可能会有人觉得应该输出3的,但是为什么却是输出2呢?
根据前面所提到的词法作用域指的是作用域由书写代码时函数的位置来决定的,而不是代码执行引用的位置。只要理解了这个概念,就能明白了上面的输出结果了。
函数foo是在全局作用域下定义的,因此它的作用域是属于全局作用域的子作用域。函数bar也是一样,属于全局作用域的子作用域。
虽然函数foo在函数bar中执行了(二者并不存在父子作用域的关系),但是函数fn此时的父级作用域仍然是全局作用域。因此当引擎执行console.log(a)的声明时,会对变量a进行RHS查询。引擎在foo当前作用域寻找不到a,便往上级作用域(对foo而言便是全局作用域)寻找,在上级作用域找到了a=2,因此就输出了2
这也就是JavaScript没有动态作用域的原因。如果JavaScript支持动态作用域,那么最后的输出结果应该是3,而不是2