Lexical Scope - 詞法做用域java
做用域有兩種常見的模型,一種叫作 詞法做用域 Lexical Scope,一種叫作動態做用域 Dynamic Scope。其中詞法做用域更常見,被大多數語言採用,包括javascript。git
詞法分析過程Lex-time,是指系統講源碼字符串解讀成有含義的token的過程。詞法做用域就是說在詞法分析過程當中指派的做用域,詞法做用域在詞法解析過程當中就已經定死了。github
依然有一些手段能在詞法解析以後改變詞法做用域,但這些作法並不推薦。使用關鍵詞eval
, with
,會產生性能問題。瀏覽器
考慮以下代碼:
函數
foo
a
,bar
,b
c
bar
做用域裏完整的包含了foo
的做用域, 由於bar 是在foo中定義的,產生嵌套做用域。值得注意的是,一個函數做用域只有可能存在於一個父級做用域中,不會同時存在兩個父級做用域。性能
過程:this
語句console.log
尋找變量a,b,c 其中c在本身的做用域中找到,a,b在本身的做用域中找不到,因而向上級做用域中查找,在foo的做用域中找到,而且調用。做用域向上查找的過程當中,匹配第一次查找到的變量,也就是說若是foo的做用域中也定義了c,但bar函數只調用本身做用域裏的c。spa
做用域的查找一直會找到全局做用域的全局對象,好比瀏覽器中的window,你能夠定義window.a
來確保變量a能夠被獲取。code
詞法做用域向上查找只查找第一級變量,好比變量foo.bar.baz
, 查找的是對象foo
,查找到了再從對象裏獲取bar以及baz,查找自己與bar,baz無關。
動態做用域,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
也關心函數在哪裏調用的。