静态作用域与动态作用域

静态作用域与动态作用域

作用域有两种常见的模型:词法作用域(Lexical Scope,通常也叫做 静态作用域) 和 动态作用域(Dynamic Scope)。其中词法作用域更常见,被 JavaScript 等大多数语言采用。(愚人码头注:这里避开了with和eval特殊语句,不再做介绍)。

静态作用域与动态作用域:

  • 词法作用域:词法作用域是指在词法分析阶段就确定了,不会改变。变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。

  • 动态作用域:动态作用域是在运行时根据程序的流程信息来动态确定的,而不是在写代码时进行静态确定的。 动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心它们在何处调用。

JavaScript的词法作用域:

如果一个文档流中包含多个script代码段(用script标签分隔的js代码或引入的js文件),它们的运行顺序是:

  1. 读入第一个代码段(js执行引擎并非一行一行地分析程序,而是一段一段地分析执行的)
  2. 做词法分析,有错则报语法错误(比如括号不匹配等),并跳转到步骤5
  3. 对var变量和function定义做“预解析“(永远不会报错的,因为只解析正确的声明)
  4. 执行代码段,有错则报错(比如变量未定义)
  5. 如果还有下一个代码段,则读入下一个代码段,重复步骤2
  6. 完成
1
2
3
4
5
6
7
8
9
10
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar();
// 结果是 ???

假设JavaScript采用静态作用域,让我们分析下执行过程:(这部分要结合作用域链看)

执行 foo 函数,先从 foo 函数局部作用域中查找是否有变量 value,如果没有,就从全局作用域中查找变量value的值,所以结果会打印 1。

假设JavaScript采用动态作用域,让我们分析下执行过程:

执行 foo 函数,依然是从 foo 函数内部查找是否有局部变量 value。如果没有,就从调用函数的作用域,也就是 bar 函数内部查找 value 变量,所以结果会打印 2。

前面我们已经说了,JavaScript采用的是静态作用域,所以这个例子的结果是 1。

参考

实例分析 JavaScript 作用域, 同时讲了形参, 实参, 同名局部变量