1. 程式人生 > >關於作用域、防止作用域汙染、作用域鏈和閉包的理解

關於作用域、防止作用域汙染、作用域鏈和閉包的理解

作用域

變數的作用域無非就是兩種:全域性作用域和區域性作用域。  全域性作用域:  最外層函式定義的變數擁有全域性作用域,即對任何內部函式來說,都是可以訪問的:

   <script>
      var outerVar = "outer";
      function fn(){
         console.log(outerVar);
      }
      fn();//result:outer
   </script>

區域性作用域:  和全域性作用域相反,區域性作用域一般只在固定的程式碼片段內可訪問到,而對於函式外部是無法訪問的,最常見的例如函式內部

<script>
      function fn(){
         var innerVar = "inner";
      }
      fn();
      console.log(innerVar);// ReferenceError: innerVar is not defined
</script>

 需要注意的是,函式內部宣告變數的時候,一定要使用var命令。如果不用的話,實際上聲明瞭一個全域性變數!

以下是一個重要的特性!!

只要函式內定義了一個區域性變數,函式在解析的時候都會將這個變數“提前宣告”,舉例:

   <script>
      var scope = "global";
      function fn(){
         console.log(scope);//result:undefined
         var scope = "local";
         console.log(scope);//result:local;
      }
      fn();
   </script>

第一個輸出居然是undefined,原本以為它會訪問外部的全域性變數(scope=”global”),但是並沒有。這可以算是javascript的一個特點,只要函式內定義了一個區域性變數,函式在解析的時候都會將這個變數“提前宣告”。

   <script>
      var scope = "global";
      function fn(){
         var scope;//提前聲明瞭區域性變數
         console.log(scope);//result:undefined
         scope = "local";
         console.log(scope);//result:local;
      }
      fn();
   </script>

但是javascript不同,並沒有所謂的塊級作用域,javascript的作用域是相對函式而言的,可以稱為函式作用域:

   <script>
      for(var i = 1; i < 10; i++){
            //coding
      }
      console.log(i); //10  
   </script>

防止作用域汙染

在多人協作時,如果定義過多的全域性變數 有可能造成全域性變數衝突,也就是全域性變數汙染問題, 儘量避免全域性變數。

可採取兩種措施:

一是定義全域性變數名稱空間,只建立一個全域性變數,並定義該變數為當前應用容器,把其他全域性變數追加在該名稱空間下

var MY={};
my.name={
big_name:"zhangsan",
small_name:"lisi"
};
my.work={
school_work:"study",
family_work:"we are"
};

 二 是利用匿名函式將指令碼包裹起來

(function(){
var exp={};
var name="aa";
exp.method=function(){
return name;
};
window.ex=exp;
})();

作用域鏈(Scope Chain)

那什麼是作用域鏈?  我的理解就是,根據在內部函式可以訪問外部函式變數的這種機制,用鏈式查詢決定哪些資料能被內部函式訪問。  想要知道js怎麼鏈式查詢,就得先了解js的執行環境

執行環境(execution context)

js為每一個執行環境關聯了一個變數物件。環境中定義的所有變數和函式都儲存在這個物件中。 

js的執行順序是根據函式的呼叫來決定的,當一個函式被呼叫時,該函式環境的變數物件就被壓入一個環境棧中。而在函式執行之後,棧將該函式的變數物件彈出,把控制權交給之前的執行環境變數物件。 

   <script>
      var scope = "global"; 
      function fn1(){
         return scope; 
      }
      function fn2(){
         return scope;
      }
      fn1();
      fn2();
   </script>

上面程式碼執行情況演示: 

 

閉包

閉包有兩個作用:  第一個就是可以讀取自身函式外部的變數(沿著作用域鏈尋找)  第二個就是讓這些外部變數始終儲存在記憶體中 (容易導致記憶體洩漏)

關於this物件

關於閉包經常會看到這麼一道題:

  var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());//result:The Window

 《javascript高階程式設計》一書給出的解釋是:

this物件是在執行時基於函式的執行環境繫結的:在全域性函式中,this等於window,而當函式被作為某個物件呼叫時,this等於那個物件。不過,匿名函式具有全域性性,因此this物件同常指向window。