作用域和作用域鏈

2021-09-17 19:49:41 字數 3656 閱讀 1836

執行環境定義了變數和函式有權訪問的其他資料,決定了他們各自的行為。每個執行環境都有與之對應的變數物件(variable object),儲存著該環境中定義的所有變數和函式。我們無法通過**來訪問變數物件,但是解析器在處理資料時會在後台使用到它。

執行環境有全域性執行環境(也稱全域性環境)和函式執行環境之分。執行環境如其名是在執行和執行**的時候才存在的,所以我們執行瀏覽器的時候會建立全域性的執行環境,在呼叫函式時,會建立函式執行環境。

1.1 全域性執行環境

全域性執行環境是最外圍的乙個執行環境,在web瀏覽器中,我們可以認為他是window物件,因此所有的全域性變數和函式都是作為window物件的屬性和方法建立的。**載入瀏覽器時,全域性環境被建立,關閉網頁或者關閉瀏覽時全域性環境被銷毀。

1.2 函式執行環境

每個函式都有自己的執行環境,當執行流進入乙個函式時,函式的環境就被推入乙個環境棧中,當函式執行完畢後,棧將其環境彈出,把控制權返回給之前的執行環境。

2.1 全域性作用域(globe scope)和區域性作用域(local scope)**

全域性作用域可以在**中的任何地方都能被訪問,例如:

1 var name1="haha";

2 function changname()

7 changname();

8 console.log(name1);//haha

9 console.log(name2);//uncaught referenceerror: name2 is not defined

其中,name1具有全域性作用域,因此在第4行和第8行都會在控制台上輸出 haha。name2定義在changname()函式內部,具有區域性作用域,因此在第9行,解析器找不到變數name2,丟擲錯誤。

另外,在函式中宣告變數時,如果省略 var 操作符,那麼宣告的變數就是全域性變數,擁有全域性作用域,但是不推薦這種做法,因為在區域性作用域中很難維護定義的全域性變數。

再者,window物件的內建屬性都擁有全域性作用域。

區域性作用域一般只在固定的**片段內可以訪問得到,例如上述**中的name2,只有在函式內部可以訪問得到。

2.2 作用域鏈(scope chain)

全域性作用域和區域性作用域中變數的訪問許可權,其實是由作用域鏈決定的。

每次進入乙個新的執行環境,都會建立乙個用於搜尋變數和函式的作用域鏈。作用域鏈是函式被建立的作用域中物件的集合。作用域鏈可以保證對執行環境有權訪問的所有變數和函式的有序訪問。

作用域鏈的最前端始終是當前執行的**所在環境的變數物件(如果該環境是函式,則將其活動物件作為變數物件),下乙個變數物件來自包含環境(包含當前還行環境的環境),下乙個變數物件來自包含環境的包含環境,依次往上,直到全域性執行環境的變數物件。全域性執行環境的變數物件始終是作用域鏈中的最後乙個物件。

識別符號解析是沿著作用域一級一級的向上搜尋識別符號的過程。搜尋過程始終是從作用域的前端逐地向後回溯,直到找到識別符號(找不到,就會導致錯誤發生)。

例如:

1 var name1 = "haha";

2 function changename()

14 swapname();

15 console.log(name1);//haha

16 console.log(name2);//xixi

17 // console.log(tempname);丟擲錯誤:uncaught referenceerror: tempname is not defined

18 }

19 changname();

20 console.log(name1);

21 丟擲錯誤:uncaught referenceerror: name2 is not defined

22 丟擲錯誤:uncaught referenceerror: tempname is not defined

上述**中,一共有三個執行環境:全域性環境、changename()的區域性環境和 swapname() 的區域性環境。所以,

1.函式 swapname()的作用域鏈包含三個物件:自己的變數物件----->changename()區域性環境的變數物件 ----->全域性環境的變數物件。

2.函式changename()的作用域包含兩個物件:自己的變數物件----->全域性環境的變數物件。

就上述程式**現的變數和函式來講(不考慮**變數):

1.swapname() 區域性環境的變數物件中存放變數 tempname;

2.changename() 區域性環境的變數物件中存放變數 name2 和 函式swapname();

3.全域性環境的變數物件中存放變數 name1 、函式changename();

在swapname()的執行環境中,在執行第5句**時,解析器沿著函式 swapname()的作用域鏈一級級向後回溯查詢變數 name1,直到在全域性環境中找到變數 name1.並輸出在控制台上。同樣,在執行第6句**時,解析器沿著函式 swapname()的作用域鏈一級級向後回溯,在函式changename()的變數物件中發現變數 name2.通過**對 name1 和 name2進行交換,並輸出在控制台上,根據結果我們發現,這兩個變數的值確實交換了。因此我們可以得出結論,函式的區域性環境可以訪問函式作用域中的變數,也可以訪問和操作父環境(包含環境)乃至全域性環境中的變數。

在changename() 的執行環境中,執行第15行和第16行**時,可以正確地輸出 name1 和 name2 和兩個變數的值(呼叫了函式swapname(),所以倆變數的值已相互交換),那是因為 name1 在changname()的父環境(全域性環境)中, name2 在他自己的區域性環境中,即 name1 和 name2 都在其作用域鏈上。但當執行第17行**是發生錯誤 tempname is not defined。因為解析器沿著 函式changename()的作用域鏈一級級的查詢 變數 tempname時,並不能找到該變數的存在(變數 tempname不在其作用域鏈上),所以丟擲錯誤。因此,我們可以得出結論:父環境只能訪問其包含環境和自己環境中的變數和函式,不能訪問其子環境中的變數和函式

同理,在全域性環境中,其變數物件中只存放變數 name1 、函式changename(); 解析器只能訪問變數 name1 和函式 changename(), 而不能訪問和操作 函式 changename() 和函式 swapname() 中定義的變數或者函式。因此,在執行第21行和第22行**時丟擲變數沒有定義的錯誤。所以說,全域性環境只能訪問全域性環境中的變數和函式,不能直接訪問區域性環境中的任何資料。

1.執行環境決定了變數的生命週期,以及哪部分**可以訪問其中變數

2.執行環境有全域性執行環境(全域性環境)和區域性執行環境之分。

3.每次進入乙個新的執行環境,都會建立乙個用於搜尋變數和函式的作用域鏈

4.函式的區域性環境可以訪問函式作用域中的變數和函式,也可以訪問其父環境,乃至全域性環境中的變數和環境。

5.全域性環境只能訪問全域性環境中定義的變數和函式,不能直接訪問區域性環境中的任何資料。

6.變數的執行環境有助於確定應該合適釋放記憶體。

作用域和作用域鏈

全域性作用域 全域性的變數執行環境 函式作用域 函式內部的變數執行環境 每個函式都有自己的執行環境,當執行流進入乙個函式時,函式的環境就會被推入乙個環境棧中。函式執行之後,棧將環境彈出,把控制權返回給之前的執行環境。全域性作用域只要頁面不解除安裝,就一直存在,不釋放。函式每次在呼叫時,都會形成乙個作...

作用域和作用域鏈

var a 10 function f1 x,y f1 console.log b 此時b輸出5,因為b 5為隱式的全域性變數 在函式外面使用var 宣告的變數都是全域性變數,作用範圍是程式執行的地方.全域性作用域 函式除外 函式的形參是區域性變數 在函式內部,不使用var 定義的變數,叫隱式全域性...

作用域和作用域鏈

作用域與作用域鏈 作用域屬於乙個函式,乙個函式產生了不一樣的作用域 函式名.scope 函式的隱式屬性 scope 指的就是作用域,其中儲存了執行期的上下文的集合,而這一集合就是作用域鏈 查詢變數 從作用域鏈的頂端依次向下查詢 在那個函式裡面查詢變數,就上那個函式的作用域鏈的頂端依次向下查詢變數 2...