ES6-let、const和塊級作用域


  1.介紹 

  總的來說,ES6是在ES2015的基礎上改變了一些書寫方式,開放了更多API,這樣做的目的最終還是為了貼合實際開發的需要。如果說一門編程語言的誕生是天才的構思和實現,那它的發展無疑就是不斷填坑的歷史。ES6正是為了填一些坑。

  我對ES6語法的學習,主要在瀏覽器端,參考阮一峰大神的ES6入門教程,添加了一些個人理解的注釋和遇到的小問題,歡迎批評指正,共同進步。

   瀏覽器端引用的依賴文件和本文案例  可在 https://github.com/chanceLe/ES6-Basic-Syntax 找到。

       2.基礎部分

       2.1let和const關鍵字

        let命令聲明變量,類似var,但所聲明的變量只在let代碼塊有效。  

           {
                let a = 1;
                var b = 2;
            }
            console.log(b);
            console.log(a);  //報錯,找不到。
            //for循環的計數器,就很適合用let
            for(let i=0;i<5;i++){
                console.log(i);
            }
            //上面的代碼的計數器i,只在for循環體內有效。

    {}在ES6中用來形成塊級作用域,后邊會說到。  

      var a = [];
          for(var i=0;i<10;i++){
              a[i] = function(){
                   console.log(i);
                }
            }
        
      a[6]();  //10  到九循環完,又加了1,換成let會得到期望結果。

  var聲明的變量全局范圍內都有效。所以每次循環,新的i值都會覆蓋舊值
  let聲明的僅在塊級作用域內有效,最后輸出6

   let不會發生變量提升的現象,所以一定要在定義后使用,否則報錯。

  暫時性死區:只要塊級作用域內存在let命令,它所聲明的變量就綁定這個區域,不再受外部影響。

       var tmp = 123;
            if(true){
                tmp = "abc";
                console.log(tmp,"11");  //"abc"
                let tmp;    
            }
            console.log(tmp,"22");  //"123"

  書上說,如果區塊中存在let和const命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉
  作用域,凡是聲明之前使用的,都會報錯。


  實際情況,let不會報錯,const會報錯(這可能跟瀏覽器端轉碼有關,版本為5)。

  有些死區比較隱蔽,不太容易發現:

       function bar(x=y,y=2){
                return [x,y];
            }
            console.log(bar());   //書上說這里由於y沒有定義,應該報錯,實際是 [undefined,2]
            
            
            function bar2(x=2,y=x){
                return [x,y];
            }
            console.log(bar2());   //[2,2]正常

  不允許重復聲明,let不允許在相同作用域內,重復聲明同一個變量:

          function fun(){
                let a = 10;
         //     var a = 5;   //報錯。
         //     let a = 1;   //報錯
            }  

  不能在函數內部重新聲明參數:

      function fun1(arg){
      //    let arg;   //報錯
                {
                    let arg;  //不報錯
                }
            }

  const命令
   聲明一個只讀的常量。一旦聲明,常量的值就不能改變。
   同時也說明,一旦聲明,就要立即初始化,否則也報錯。

       const PI = 3.1415;
            console.log(PI);   
//          PI = 3;  //報錯 只讀的
//          const circle;   //報錯,一旦聲明,必須立即初始化。
               /*
                * const的作用域與let命令相同:只在聲明所在的塊級作用域有效。
                */
             if(true){
                 const m = 5;
             }
//           console.log(m); //未定義
    

  與let相似:
   const命令聲明的常量也不提升,同樣存在暫時性死區,只能在聲明的位置后使用。
   也不可以重復聲明。

   對於復合類型的變量,變量名不指向數據,而是指向數據所在的地址。const命令只
   是保證變量名指向的地址不變,並不保證該地址的數據不變,所以將一個對象聲明為常量,
   必須非常小心。

       const foo = {};
            foo.prop = 123;
            console.log(foo.prop);
            //上面的常量foo存儲的是對象的地址,這個地址不可變,但依然可以添加屬性。
            const a = [];
            a.push("hello");
            a.length = 0;
//          a = ["Dave"]   //報錯,賦值就是更換地址,不行的。
            //如果真的想把對象凍結,應該使用Object.freeze方法。
            const foo2 = Object.freeze({});
            //常規模式下,下面一行不起作用
            //嚴格模式下,會報錯
            foo.prop = 123;  

  ES5只有兩種聲明變量的方法:var和function。  
  ES6有6種:var function let const class import


   全局對象屬性:
   全局對象是最頂層的對象,在瀏覽器環境下指的是window對象,在node指的是
   global對象。ES5中,全局對象的屬性和全局變量是等價的。
   未聲明的全局變量,自動成為全局對象window的屬性,這被認為是js的最大敗筆。
   ES6為了改變這一點,一方面規定,為了保持兼容性,var命令和function命令聲明的全局變量,
   依舊是全局對象的屬性,另一方面規定,let,const,class命令聲明的全局變量不屬於全局對象的
   屬性。也就是說,從ES6開始,全局變量將逐漸與全局對象的屬性脫鈎。

 

  2.2塊級作用域:

  ES5只有全局作用域和函數作用域,沒有塊級作用域,有很多不合理場景:
  1.內層變量可能會覆蓋外層變量。

       var tmp = new Date();
            function f(){
                console.log(tmp);
                if(false){
                    var tmp = "hello world!";
                }
            }
            f();  //undefined 聲明提升,后邊的定義把前邊的覆蓋掉了。

  2.用來計數的循環變量泄漏為全局變量。典型的var定義的循環,上邊有代碼體現。    

  let實際上為js新增了塊級作用域。下面的代碼兩個代碼塊都聲明了n,運行輸出5,表示外層代碼不受內層代碼塊的影響。如果是var定義,就會收到影響,輸出10;

       function f1(){
                let n = 5;
                if(true){
                    let n = 10;
                }
                console.log(n);
            }
            f1();
      //es6允許塊級作用域的任意嵌套:
            {{{{{ let insane = "hello world" }}}}}
            //上面代碼使用了五層塊級作用域,外部不能訪問內部的變量。
            //塊級作用域實際上使得廣泛應用的立即執行匿名函數(IIFE)不再需要了。
            //IIFE寫法
            (function(){
                
            })()
            //塊級作用域寫法
            {
                
            }
            /*
       * 塊級作用域與函數聲明:
             * ES5規定,函數只能在頂層作用域和函數作用域之中聲明,不能在塊作用域中聲明,
             *   情況一:
             *   if(true){
             *     function(){}
             * }
             *   情況二:
             *   try{
             *       function(){}
             * }

  這兩種情況,根據ES5的規定都是非法的。但是瀏覽器沒有遵守這個規定,可以運行,在嚴格
  模式下,還是會報錯。


   ES6引入了塊級作用域,明確允許在塊級作用域中聲明函數。
   塊級作用域中,函數聲明語句的行為類似於let,在塊級作用域之外,不可以引用。
   因為塊級作用域對作用域之外沒有影響,這個差異會對老代碼產生很大影響,為了減輕這個不兼容
   問題,ES6在附錄B中規定,瀏覽器可以不遵守上面的規定,有自己的行為方式。


   1.允許在塊級作用域內聲明函數。
   2.函數聲明類似於var,即會提升到全局作用域或函數作用域的頭部。
   3.同時,函數聲明還會提升到所在塊級作用域的頭部。
   上面的3個規則只對ES6 的瀏覽器實現有效,其他環境的實現不用遵守,還是將塊級作用域當做let處理。

   考慮到環境導致的行為差異太大,應該避免在塊級作用域內聲明函數。如果確實需要,也應該寫成函數表達式。
   ES6的塊級作用域允許聲明函數的規則,只在使用大括號的情況下成立,沒有使用大括號,會報錯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM