JavaScript之執行環境與作用域

執行環境與作用域

執行環境是JavaScript中重要的概念。執行環境定義了 變量或函數 是否有權訪問其它數據,決定了他們各自的行爲。每個執行環境都有一個與之相關的變量對象,變量對象保存了執行環境中定義的所有變量和函數

全局執行環境是最外圍的一個執行環境,全局執行環境被認爲是window對象,所有的全局變量和函數都是作爲window的屬性和方法創建的。當一個執行環境中的所有代碼執行完畢後,該環境被銷燬,該環境中的所有變量和函數定義也會被銷燬。注:全局環境 會在應用程序退出時、或關閉瀏覽器後被銷燬。


每個函數都有一個執行環境,當執行流進入到函數時,函數的執行環境會被推入一個環境棧,地函數執行完畢後,其執行環境被推出環境棧,把控制權返回給之前的執行環境。

當代碼在執行環境中執行,會創建變量對象的一個作用域鏈作用域鏈的作用是 對執行環境有權訪問變量和函數的有序訪問作用域鏈前端,就是當前執行的代碼所在執行環境的變量對象。如果這個環境是函數,那麼該函數的活動對象就是變量對象。活動對象在剛開始時,只包含一個變量對象,即argument對象,下一個變量對象來自包含環境,再下一個變量對象來自下一個包含環境,直至全局執行環境,全局執行環境的變量對象始終是作用域鏈的最後一個對象。(也可以說,一個函數的作用域鏈上,至少包含兩個變量對象,即自身的變量對象和全局環境的變量對象)


標識符解析是沿着作用域鏈一級一級查找標識符的過程。搜索從作用域鏈前端開始,然後逐級身後查找 ,直至找到標識符爲止,如果找不到,則報錯。

<!doctype html>  
<html>  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
      
    
  <script>  
        
      var color = "blue"; //全局作用域,color保存在全局執行環境的變量對象中。

      function changColor () { //changColor的執行環境
          if (color == "blue") {
              color = "red";
          } else {
              color = "blue";
          }
      }

      changColor();
      console.log(color);
        
  </script>  
  </body>  
</html>  


這個例子中,changColor()的作用域包含兩個變量對象:自身的變量對象(開始時,只包含一個變量對象即argument對象)和全局執行環境中的變量對象(下一個變量對象就是包含環境中的變量對象)。這裏changColor()只被一個全局環境包含,所以只有一個全局環境中的變量對象和自身的變量對象。在函數內部可以訪問到color,是因爲全局環境的變量對象(包含color變量)在changColor()的作用域鏈上。

其此,在局部作用域中定義的變量可以在局部環境中和全局變量互換使用。

<!doctype html>  
<html>  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
      
    
  <script>  
        
      var color = "blue"; //全局作用域,color保存在全局執行環境的變量對象中。

      function changColor () {
          var color1 = "red";

          function authColor () {
              var color2 = color1;
              color1 = color;
              color = color2;

              //這裏面可以訪問color、color1、color2
          }

          //這裏面可以訪問color1、color
      }
      
      //外面只能訪問color
              
  </script>  
  </body>  
</html>  


這個例子中有3個執行環境:全局執行環境、changColor()局部環境、authColor()局部環境。在全局環境中有一個變量color和一個changColor()函數,changColor()局部環境中有一個變量color1和一個authColor()函數,ahthColor()局部環境中有一個變量color2。

在authColor()的作用域鏈上有三個變量對象,分別是:自身的變量對象、changColor()局部環境中的變量對象、全局環境中的變量對象。注:變量對象保存着執行環境中定義的變量和函數 。

在changColor()的作用域上有兩個變量對象,分別是:自身的變量對象、全局環境中的變量對象。

在全局環境作用域上只有有一個自身的變量對象。

從上可以說明,在authColor()函數中可以訪問color2、color1和color,因爲這三個變量均在authColor()函數的作用域鏈上。在changColor()函數中可以訪問color1和color,因爲這兩個變量均在changColor()函數的作用域鏈上。在全局環境中只能訪問color和changColor()函數,因爲color和changColor()函數在全局作用域鏈上。

上例中的執行環境作用域鏈關係圖:



從上圖可以看出,內部環境可以通過作用域鏈訪問外部環境(即環境中的變量和函數),但外部環境不能訪問內部環境中的變量和函數。環境之間的關係是線性、有次序的。每個環境可以向上搜索作用域鏈,以查找標識符和函數,在不能向下搜索作用域鏈。就拿authColor()函數來說,其作用域上有三個變量對象:自身的變量對象、changColor()函數環境中的變量對象、全局環境中的變量對象,authColor()首先會在自身的環境中查找標識符,如果沒有查找到就會逐級向上一個環境作用域鏈中查找,直至查找到爲止。全局環境是它查找的終點,如果在全局環境中也沒有查找到標識符,那麼就會報錯。

注:函數參數也會被當作變量來對待,訪問規則與執行環境中的其它變量一樣。


延長作用域鏈

執行環境有兩種:全局作用和局部作用。我們使用其它方法來延長作用域鏈。有些語句可以在作用域鏈前端增加一個變量對象,該變量對象在代碼執行完畢後被銷燬。

有兩種方法來延長作用域鏈:

1、try-catch語句中的catch塊

2、with語句


沒有塊級作用域

JavaScript不像其它語言有自己的塊級作用域,而JavaScript有自己的執行環境。

<!doctype html>  
<html>  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
      
    
  <script>  

      if (true) {
          var color = "blue";
      }
    
      console.log(color); //blue
           
  </script>  
  </body>  
</html>  


上例在其它語言中,color會在if語句執行完畢後被銷燬,而在JavaScript中,if語句執行完畢後被銷燬,但其變量聲明會把變量提到當前的執行環境中(也可以說是上一級執行環境,這裏是全局執行環境,函數執行結束後,函數的執行環境被彈出環境棧,控制權已經返回給了之前的執行環境--全局執行環境)。


JavaScript中的for循環中初始化變量的表達式定義的變量也會存在於循環外部的執行環境中(for循環的上一級執行環境)。

<!doctype html>  
<html>  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
      
    
  <script>  

      for (var i = 0; i < 10; i++) {
          //執行一些代碼
      }
      console.log(i); //10 變量i已經在for循環的外部執行環境中
          
  </script>  
  </body>  
</html>  



聲明變量

使用關鍵字var聲明的變量,會自動被添加到最接近的執行環境中。如果在函數內部,最接近的環境就是函數局部環境,不使用var聲明的變量,會被添加到全局執行環境中。

<!doctype html>  
<html>  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
      
    
  <script>  

      function add(num1, num2) {
          var sum = num1 + num2;
          return sum;
      }

      var sum1 = add(10, 10);
      console.log(sum); //報錯,sum is not defined
          
  </script>  
  </body>  
</html>  


例子中用var定義的變量會被添加到add()函數的局部環境中,會添加到add()函數的作用域鏈上,外部的執行環境是訪問不到局部環境中的sum變量的,因此會報錯。


如果不用var定義變量,那麼該變量會被添加到全局執行環境中,因此add()函數的外部環境就能訪問到了。

<!doctype html>  
<html>  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
      
    
  <script>  

      function add(num1, num2) {
          sum = num1 + num2;
          return sum;
      }

      var sum1 = add(10, 10);
      console.log(sum); //20
          
  </script>  
  </body>  
</html>  



我們在寫代碼的過程中,第二種不用關鍵字var定義變量的方式不建議使用,因爲這樣可能會導致出錯。


查詢標識符

當在某個執行環境中爲了讀取或訪問而引入一個標識符,必須通過搜索來確定這個標識符具體代表什麼,或者說是否存在。搜索過程是從作用域鏈前端(當前執行的代碼所在環境中的變量對象)開始的,然後逐級向上查找(逐級向上一執行環境查找)與給定名字相匹配的標識符。如果在局部環境中查找到了該標識符,則搜索停止,變量就緒,如果沒有查找到,則繼續向上一執行環境查找,直至全局執行環境。如果在全局環境中還是沒有查找到,則表示給定名字的標識符沒有定義。

<!doctype html>  
<html>  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
      
    
  <script>  

      var color = "red";

      function getColor () {
          retrun color;
      }
      getColor(); //red
          
  </script>  
  </body>  
</html>  


在getColor()函數中引用了color變量,爲了確定color的值,就會從作用域鏈前端開始查找,首先會在getColor()函數局部環境中查找,是否有名爲color的標識符,如果沒有,則繼續向上一級環境中查找(即在全局環境),然後在全局環境中查找到了名爲color的標識符。


如果此時,我們在getColor()局部環境中定義一個名爲color的變量,且賦值不同的值,那麼返回的結果就不一樣了。

<!doctype html>  
<html>  
  <head>  
    <meta charset="utf-8">  
    <title>JavaScript</title>  
  </head>  
  <body>  
      
    
  <script>  

      var color = "red";

      function getColor () {
          var color = "blue";
          retrun color;
      }
      getColor(); //blue
          
  </script>  
  </body>  
</html>  


此時返回的結果不再是"red',而是"blue"。因爲在搜索的過程中,在局部環境中可以查找到名爲color的標識符,那麼搜索就會停止,並確定color的值。



垃圾收集

JavaScript具有自動回收垃圾的機制,也就是說,開發人員不用擔心內存的問題,JavaScript會自動消除垃圾和釋放內存。清除垃圾的方法有兩種:


標記清除

當變量進入執行環境時,就將它們標記爲"f進入環境",當它們離開執行環境時,就將它們標記爲"離開環境"。

垃圾收集器會在運行時爲內存中的變量加上標記,然後,它會去掉環境中的變量和被環境中的變量引用的變量的標記,在此以後再被加上標記的變量將被視爲準備刪除的變量。垃圾回收器完成清除垃圾的工作,銷燬帶有標記的值以及釋放其所佔用的內存空間。



管理內存


執行的代碼中只保存必要的值,對於不必要的值或者對象,將其賦值null,這樣可以做到釋放內存的作用。將內存留給其它的功能使用。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章