静态作用域与动态作用域
作用域有两种常见的模型:词法作用域(Lexical Scope
,通常也叫做 静态作用域) 和 动态作用域(Dynamic Scope
)。其中词法作用域更常见,被 JavaScript
等大多数语言采用。(愚人码头注:这里避开了with和eval特殊语句,不再做介绍)。
静态作用域与动态作用域:
词法作用域:词法作用域是指在词法分析阶段就确定了,不会改变。变量的作用域是在定义时决定而不是执行时决定,也就是说词法作用域取决于源码,通过静态分析就能确定,因此词法作用域也叫做静态作用域。
动态作用域:动态作用域是在运行时根据程序的流程信息来动态确定的,而不是在写代码时进行静态确定的。 动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心它们在何处调用。
JavaScript的词法作用域:
如果一个文档流中包含多个script代码段(用script标签分隔的js代码或引入的js文件),它们的运行顺序是:
- 读入第一个代码段(js执行引擎并非一行一行地分析程序,而是一段一段地分析执行的)
- 做词法分析,有错则报语法错误(比如括号不匹配等),并跳转到步骤5
- 对var变量和function定义做“预解析“(永远不会报错的,因为只解析正确的声明)
- 执行代码段,有错则报错(比如变量未定义)
- 如果还有下一个代码段,则读入下一个代码段,重复步骤2
- 完成
1 | var value = 1; |
假设JavaScript采用静态作用域,让我们分析下执行过程:(这部分要结合作用域链看)
执行 foo
函数,先从 foo
函数局部作用域中查找是否有变量 value
,如果没有,就从全局作用域中查找变量value
的值,所以结果会打印 1。
假设JavaScript采用动态作用域,让我们分析下执行过程:
执行 foo
函数,依然是从 foo
函数内部查找是否有局部变量 value
。如果没有,就从调用函数的作用域,也就是 bar
函数内部查找 value
变量,所以结果会打印 2。
前面我们已经说了,JavaScript采用的是静态作用域,所以这个例子的结果是 1。