JavaScript 深入之詞法作用域和動態作用域

發表於2017-05-04

作用域

作用域是程式原始碼中定義變數的區域。

作用域規定了如何查詢變數,也就是確定當前執行程式碼對變數的訪問許可權。

ECMAScript6之前只有全域性作用域和函式作用域。

JavaScript採用詞法作用域(lexical scoping),也就是靜態作用域。

靜態作用域與動態作用域

因為採用詞法作用域,函式的作用域在函式定義的時候就決定了。

與詞法作用域相對的是動態作用域,函式的作用域在函式呼叫的時候才決定。

讓我們認真看個例子就能明白之間的區別:

當採用靜態作用域時,執行foo函式,先從foo函式內部查詢是否有區域性變數value,如果沒有,就根據書寫的位置,查詢上面一層的程式碼,在這裡是全域性作用域,也就是value等於1,所以最後會列印1

當採用動態作用域時,執行foo函式,依然是從foo函式內部查詢是否有區域性變數value。如果沒有,就從呼叫函式的作用域,也就是bar函式內部查詢value變數,所以最後會列印2

動態作用域

也許你會好奇什麼語言是動態作用域?

bash就是動態作用域,不信的話,把下面的指令碼存成例如scope.bash,然後進入相應的目錄,用命令列執行 bash ./scope.bash,看看列印的值是多少

這個檔案也可以在demos/scope/中找到。

思考題

最後,讓我們看一個《JavaScript權威指南》中的例子:

猜猜兩段程式碼各自的執行結果是多少?

這裡直接告訴大家結果,兩段程式碼都會列印’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,對作者也是一種鼓勵。

相關文章