Lexical Scope - 词法作用域
作用域有两种常见的模型,一种叫做 词法作用域 Lexical Scope,一种叫做动态作用域 Dynamic Scope。其中词法作用域更常见,被大多数语言采用,包括javascript。
词法分析
词法分析过程Lex-time,是指系统讲源码字符串解读成有含义的token的过程。词法作用域就是说在词法分析过程中指派的作用域,词法作用域在词法解析过程中就已经定死了。
依然有一些手段能在词法解析之后改变词法作用域,但这些做法并不推荐。使用关键词eval
, with
,会产生性能问题。
考虑如下代码:
- 气泡(作用域) 1 :即全局作用域,包含变量
foo
- 气泡(作用域) 2 :foo的作用域,包含变量
a
,bar
,b
- 气泡(作用域) 3 :bar的作用域,包含变量
c
bar
作用域里完整的包含了foo
的作用域, 因为bar 是在foo中定义的,产生嵌套作用域。值得注意的是,一个函数作用域只有可能存在于一个父级作用域中,不会同时存在两个父级作用域。
过程:
语句console.log
寻找变量a,b,c 其中c在自己的作用域中找到,a,b在自己的作用域中找不到,于是向上级作用域中查找,在foo的作用域中找到,并且调用。作用域向上查找的过程中,匹配第一次查找到的变量,也就是说如果foo的作用域中也定义了c,但bar函数只调用自己作用域里的c。
作用域的查找一直会找到全局作用域的全局对象,比如浏览器中的window,你可以定义window.a
来确保变量a可以被获取。
词法作用域向上查找只查找第一级变量,比如变量foo.bar.baz
, 查找的是对象foo
,查找到了再从对象里获取bar以及baz,查找本身与bar,baz无关。
动态作用域 Dynamic Scope
动态作用域,javascript并无采用,但是与js的 this
机制非常相似,看如下代码:
动态作用域是在代码运行时定义的,而非代码解析时。
function foo() {
console.log( a );
}
function bar() {
var a = 3;
foo();
}
var a = 2;
bar();
bar调用,bar里面foo被调用,foo函数需要查找变量a,由于javascript采用词法作用域,foo被解析的时候是在全局作用域,所以a是全局作用域中的2,而非bar里面的a。假设js采用的是动态作用域,foo是在bar中被调用的,所以a查找到了bar作用域里的3。
作为对照,动态作用域不关心它本身是怎样在哪里声明的,只关心它在哪里调用的,动态作用域的域链基于调用栈,而不是代码中的嵌套关系。
相反,词法作用域关心的是函数在哪里声明的,动态作用域的概念和js中的this
相同,this
也关心函数在哪里调用的。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。