作用域
作用域是程式原始碼中定義變數的區域。
作用域規定了如何查詢變數,也就是確定當前執行程式碼對變數的訪問許可權。
ECMAScript6之前只有全域性作用域和函式作用域。
JavaScript採用詞法作用域(lexical scoping),也就是靜態作用域。
靜態作用域與動態作用域
因為採用詞法作用域,函式的作用域在函式定義的時候就決定了。
與詞法作用域相對的是動態作用域,函式的作用域在函式呼叫的時候才決定。
讓我們認真看個例子就能明白之間的區別:
1 2 3 4 5 6 7 8 9 10 11 12 |
var value = 1; function foo() { console.log(value); } function bar() { var value = 2; foo(); } bar(); |
當採用靜態作用域時,執行foo函式,先從foo函式內部查詢是否有區域性變數value,如果沒有,就根據書寫的位置,查詢上面一層的程式碼,在這裡是全域性作用域,也就是value等於1,所以最後會列印1
當採用動態作用域時,執行foo函式,依然是從foo函式內部查詢是否有區域性變數value。如果沒有,就從呼叫函式的作用域,也就是bar函式內部查詢value變數,所以最後會列印2
動態作用域
也許你會好奇什麼語言是動態作用域?
bash就是動態作用域,不信的話,把下面的指令碼存成例如scope.bash,然後進入相應的目錄,用命令列執行 bash ./scope.bash,看看列印的值是多少
1 2 3 4 5 6 7 8 9 |
value=1 function foo () { echo $value; } function bar () { local value=2; foo; } bar |
這個檔案也可以在demos/scope/中找到。
思考題
最後,讓我們看一個《JavaScript權威指南》中的例子:
1 2 3 4 5 6 7 8 9 |
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f(); } checkscope(); |
1 2 3 4 5 6 7 8 9 |
var scope = "global scope"; function checkscope(){ var scope = "local scope"; function f(){ return scope; } return f; } checkscope()(); |
猜猜兩段程式碼各自的執行結果是多少?
這裡直接告訴大家結果,兩段程式碼都會列印’local scope’。
引用權威指南的回答就是:
JavaScript函式的執行用到了作用域鏈,這個作用域鏈是在函式定義的時候建立的。巢狀的函式f()定義在這個作用域鏈裡,其中的變數scope一定是區域性變數,不管何時何地執行函式f(),這種繫結在執行f()時依然有效。
但是在這裡真正想讓大家思考的是,兩段程式碼執行的結果一樣,但是兩段程式碼究竟有哪些不同呢?
如果要回答這個問題的話,就要牽涉到很多的內容,詞法作用域只是其中的一小部分,讓我們期待下一篇文章————《JavaScript深入之執行上下文棧》
深入系列
JavaScript深入系列預計寫十五篇左右,旨在幫大家捋順JavaScript底層知識,重點講解如原型、作用域、執行上下文、變數物件、this、閉包、按值傳遞、call、apply、bind、new、繼承等難點概念,與羅列它們的用法不同,這個系列更注重通過寫demo,捋過程、模擬實現,結合ES規範等方法來講解。
所有文章和demo都可以在github上https://github.com/mqyqingfeng/Blog找到。如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。如果喜歡或者有所啟發,歡迎star,對作者也是一種鼓勵。