js做用域與執行環境(前端基礎系列)

1、做用域(what?)前端

  官方解釋是:「一段程序代碼中所用到的名字並不老是有效/可用的,而限定這個名字的可用性的代碼範圍就是這個名字的做用域。」
  單從文字理解比較難懂,舉個栗子:
        java

function outer(){
  // 聲明變量
  var name = "ukerxi";
  
  // 定義內部函數
  function inner() {
    console.log(name); // 能夠訪問到 name 變量
  }
}
console.log(name); // 報錯,undefined

  其中變量name聲明在 oute r函數中,當在 outer 中定義一個 inner 函數進行輸出 name,能夠獲得正確的值,而在 outer 外進行輸出 name 出現 undefined 錯誤;在此能夠看出 outer 函數即爲 name 變量的做用域(證實過程比較粗略,但結論仍是正確的--_--);編程

 

2、做用域(why?)後端

  做用域的使用,提升了程序的邏輯的局部性,加強程序的可靠性,以及避免命名衝突;爲代碼的模塊化開發提供便利;根據上面提到的函數做用域,name 變量被侷限在了 outer 函數中,在其餘的函數中也能夠定義相同名字的變量,二者之間不會互相影響;瀏覽器

 

3、js 中的做用域app

  先說ES5版本及更低版本的,由於在 ES6 上,從新定義了幾個決定做用域的關鍵字;模塊化

  1. 沒有塊級做用域
    在javaScript中,不像C、java等擁有塊級做用域;常見塊級做用域,例如:
    // C語言實現
    for(int i= 0; i<10; i++){
        // 中括號裏面就是塊級做用域
    }
    
    if(ture){   
        int i = 1;
        // 這裏也是塊級做用域
    }
    printf("%d/n",i); // --「use an undefined variable:i」
    //這裏是訪問不到for語句中的i與if語句中的i變量的

    固然這些在javaScript中是沒有的,通常來講只有塊級做用域;因此使用時須注意做用域的影響,例如:
    for(var i= 0; i<10; i++){
        // do something
    }
    
    console.log(i); // --10

    在程序設計過程當中可使用函數做用域,進行模擬塊級做用域;例如:
    function loop(){
        for(var i= 0; i<10; i++){
            // do something
        }
    }
    loop();
    console.log(i); // --undefined

    javaScript是靈活可變的,一樣上面這個例子,可使用自執行函數重寫實現,這樣就減小了調用這一步;
    (function (){
        for(var i= 0; i<10; i++){
            // do something
        }
    }());
    console.log(i); // --undefined
  2. ES6中的做用域
    在ES6中新增長了(let,const)關鍵字,進行定義變量,解決了沒有塊級做用域的限制;
    let:let容許你聲明一個做用域被限制在塊級中的變量、語句或者表達式。與var關鍵字不一樣的是,它聲明的變量只能是全局或者整個函數塊的。let聲明的變量只在其聲明的塊或子塊中可用,這一點,與var類似。兩者之間最主要的區別在於var聲明的變量的做用域是整個封閉函數。
    具體以下:
    function range () {
      // let 和var 相同的地方,都有函數做用域
      var name = 'ukerxi';
      let nameOuter = 'outer';
    
      for (var j = 0; j < 1; j++) {
        console.log(name)
      }
      console.log("輸出j變量", j); // ==> 1
      for (let i = 0; i < 1; i++) {
        console.log(nameOuter)
      }
      console.log("輸出i變量", i); // 報錯 undefined
    }

    能夠看出,使用let 定義的i變量,在for語句外進行輸出時,會進行報錯,說明i不在該做用域內,i的做用域在for包裹的做用內;函數

  3. 做用域鏈
    每一個函數都有本身的執行環境,包含當前環境的變量訪問關係,與之相關聯的就是「變量對象」,若是是當前函數的變量對象,也可稱爲「活動對象」;此對象中包含了,當前函數可訪問的變量及函數;變量對象,最開始包含的對象是參數的arguments對象,而後是在函數中定義的其餘變量及方法;例如:
    function fn(name){
        var text = "test";
    }
    // 變量對象中包含:命名函數fn變量、參數name、內部變量test


    固然這個變量對象是不可訪問的,只提供後臺引擎編譯執行使用;當定義有多個變量對象嵌套,這些變量對象就組成了做用域鏈;例如:oop

    var name = "global";
    function super() {
        var name = "super";
        function sub(){
             var name = "sub";
        }
    }

    做用域鏈:
    this

    在做用域最前端的是活動對象,而最後端是全局執行環境window(瀏覽器宿主中);變量訪問原則是,根據做用域前端往上進行搜索,若是提早搜索到變量,則中止搜索,例如上面這個例子中,name變量的值是"sub"由於其在最前端的變量對象中已經定義了,就不會往上繼續檢索;

  4. 延長做用域
    有兩種方法能夠將做用域進行延長:
    ①、try-catch 語句的catch塊
    ②、with 語句

    兩個語句都是在本來的做用域最前端進行添加一個變量對象;例如:
    var name = "global";
    function test(){
        var name = "sub";
        with(window){
            console.log(name);
        }
    }
    
    test(); // -- "global"

    做用域鏈:


    因此檢索變量時,會先在最前端的window變量對象中檢索;固然,在嚴格模式下已經禁用了with語句,編程時,最好向後兼容,廢棄使用with語句;

  5. 執行環境只與函數的聲明及定義位置有關
    當一個函數定義後其執行環境與做用域鏈就已經肯定了,不會由於執行位置改變而改變,具體例子:
    var name = "global";
    function getName(){
        console.log(name);
    }
    
    function test (){
        var name = "inner";
        getName();
    }
    
    // 執行test
    test(); // -- global

    運行test 函數,其中test 函數執行的是 getName 進行輸出 name 變量,輸出的是全局變量的信息;即當 getName 定義時就已經肯定了本身的做用域及執行環境,於是不會由於執行位置的不一樣而輸出不一樣的信息;固然有一種狀況不同,那就是靈活的 this

  6. this的動態綁定
    與做用域鏈及執行環境不一樣,this是根據執行時的接受者進行綁定的,改變this的幾種方法:
    ①、new 關鍵字
    ②、call / apply 方法
    ③、直接調用構造函數

    具體例子以下:
    // 聲明一個類
    function Person (){
        this.name = "ukerxi";
    }
    // 使用new關鍵字,使this執行新建對象
    // 實際上是構造函數默認返回this
    var men1 = new Person(); // this綁定到men1上
    
    // 聲明一個空對象,使用call/apply 進行綁定
    var men2 = {};
    Person.call(men2); // this綁定到men2上
    
    // 直接執行構造函數
    Person(); // this綁定到window上(使用嚴格模式則會報錯,this指向undefined)

     

【結束語】

   系列文章,包括了原創,翻譯,轉載等各種型的文章;一方面是爲了本身總結,另外一方面頁但願能夠共享知識;在技術方面有輸入,也要有所輸出,才能更進一步!文章基於本身的實踐、閱讀及理解,若有不合理及錯誤的地方,煩請各大佬評論指出,以便改正,感謝!

相關文章
相關標籤/搜索