咱們知道,變量對於程序來講是相當重要的,若是沒有變量存儲和訪問值,整個程序會受到限制。那麼問題來了,既然程序這麼須要變量,那麼它究竟是怎麼樣去存儲變量和使用變量的呢?存儲變量這裏暫且不提,到時候會有專門一篇博客來講明這個問題。咱們此次說的主要就是如何去使用變量。這就要牽扯到咱們今天的主題做用域上面了。
歸納的來講,做用域就是一套能讓你有序訪問變量的規則。(注意有序很重要)javascript中只有函數能封閉做用域(let函數也能綁定一個塊級做用域,這裏先不作討論).javascript
來看下面一個例子java
function foo (a) { var b = a * 2 function bar (c) { console.log(a, b, c) } bar(b * 3) console.log(c) } foo(2)
(1)foo函數內部會造成一個做用域
(2)bar函數內部會造成一個做用域
(3)有一個全局的做用域
咱們前面說過,做用域是一套能讓你有序訪問變量的規則,那麼上述代碼運行的時候,做用域是怎麼樣訪問變量的呢?來看看下面的示意圖。
,一個是函數bar的做用域,一個是函數foo的做用域,一個是全局的做用域。而且這三個做用域是嵌套的。
(1)bar做用域中有一個變量c
(2)foo做用域中有三個變量a,b,bar
(3)全局做用域中有一個變量foo
咱們來看看上面代碼的運行過程,首先執行最外層的foo(2),foo在調用棧調用bar,bar執行。可是注意bar內部的執行語句爲console.log(a,b,c)
咱們前面已經說過,bar做用域中只有變量c,那麼上述語句是否會出現錯誤呢,答案是不會。上述代碼會正常輸出。那麼爲何會這樣呢?答案就是代碼在運行的過程當中有一個做用域鏈能做用域給串起來。以下圖
內部的做用域能夠訪問外部做用域的變量。因此bar函數在執行console.log(a,b,c)時,在當前做用域中若是沒有找到a,b變量,它會順着做用域鏈往上找,在上層做用域foo中找到了a,b變量,它就會使用上層做用域a,b的值。若是上層做用域仍是沒有a,b的話,它會順着做用域繼續查找,直到全局變量。若是全局變量仍然沒有,程序就會報錯。那麼既然內部做用域能沿着做用域鏈訪問到外部做用域,那麼外部做用域能不能順着做用域鏈訪問內部做用域呢?不急,繼續看下面代碼。
執行完bar函數後,bar函數從執行棧中彈出,繼續執行foo函數剩餘的語句,console.log(c)
因爲當前做用域中不存在變量c,可是其子做用域內有變量c的定義,那麼程序會不會輸出子級做用域的變量c呢?答案是不會。
上級做用域不能經過做用鏈進入下級做用域。只有下級做用域能經過做用鏈進入上級做用域。只就是做用域的有序性。有序的訪問全部能訪問的變量和函數。函數
做用域就像一個一個封閉的空間,不一樣做用域內的變量是不會相互影響的。可是做用域之間又會有聯繫。若是是嵌套的做用域的話,這些嵌套做用域會經過做用域鏈把嵌套做用域聯繫在一塊兒。內部做用域能經過做用域鏈訪問到上級做用域的變量。即若是當前做用域中沒有某個變量,引擎會經過做用域鏈查找上級做用域看看有沒有定義該變量。直到全局做用域。(全局沒有則報錯)
可是上級做用域無法經過做用域鏈訪問下級做用域。這就是做用域的有序性。經過做用域鏈能讓引擎對執行環境裏全部有權訪問的變量和函數進行有序訪問。spa