let、const和var的區別比較詳解

前言

首先,須要知道let、const、var都是聲明變量的命令。let和const是es6中新增的。javascript

順便說下,es6中共6種聲明命令,還有function、import、class。而在es5或之前,只有var和function兩種。html

而後,須要知道es6(ECMAScript6)是2015才肯定的,es是標準,而js(javascript)是es的一個實現。java

最後,既然es6出現不是好久,必然不會所有瀏覽器都支持,目前有babel這些包能夠對es6轉爲es5。node

好,下面開始講這三者區別。es6

let和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中用來造成塊級做用域,後邊會說到。  babel

      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不會發生變量提高的現象,因此必定要在定義後使用,不然報錯。es5

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

       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;

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

  塊級做用域:

  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處理。

最後說說var

首先,ECMAScript和JavaScript關係: 
      ECMAScript是一個國際經過的標準化腳本語言。JavaScript由ECMAScript和DOM、BOM三者組成。能夠簡單理解爲:ECMAScript是JavaScript的語言規範,JavaScript是ECMAScript的實現和擴展。

1. 塊做用域{ }

JS中做用域有:全局做用域、函數做用域。沒有塊做用域的概念。ECMAScript 6(簡稱ES6)中新增了塊級做用域。 
塊做用域由 { } 包括,if語句和for語句裏面的{ }也屬於塊做用域。

<script type="text/javascript">  
    {  
        var a = 1;  
        console.log(a); // 1  
    }  
    console.log(a); // 1  
    // 可見,經過var定義的變量能夠跨塊做用域訪問到。

    (function A() {  
        var b = 2;  
        console.log(b); // 2  
    })();  
    // console.log(b); // 報錯,  
    // 可見,經過var定義的變量不能跨函數做用域訪問到

    if(true) {  
        var c = 3;  
    }  
    console.log(c); // 3  
    for(var i = 0; i < 4; i ++) {  
        var d = 5;  
    };  
    console.log(i); // 4   (循環結束i已是4,因此此處i爲4)  
    console.log(d); // 5  
    // if語句和for語句中用var定義的變量能夠在外面訪問到,  
    // 可見,if語句和for語句屬於塊做用域,不屬於函數做用域。  
</script>

2. var、let、const的區別

var定義的變量,沒有塊的概念,能夠跨塊訪問, 不能跨函數訪問。
let定義的變量,只能在塊做用域裏訪問,不能跨塊訪問,也不能跨函數訪問。
const用來定義常量,使用時必須初始化(即必須賦值),只能在塊做用域裏訪問,並且不能修改。

<script type="text/javascript">  
    // 塊做用域  
    {  
        var a = 1;  
        let b = 2;  
        const c = 3;  
        // c = 4; // 報錯  
        var aa;  
        let bb;  
        // const cc; // 報錯  
        console.log(a); // 1  
        console.log(b); // 2  
        console.log(c); // 3  
        console.log(aa); // undefined  
        console.log(bb); // undefined  
    }  
    console.log(a); // 1  
    // console.log(b); // 報錯  
    // console.log(c); // 報錯

    // 函數做用域  
    (function A() {  
        var d = 5;  
        let e = 6;  
        const f = 7;  
        console.log(d); // 5  
        console.log(e); // 6  (在同一個{ }中,也屬於同一個塊,能夠正常訪問到)  
        console.log(f); // 7  (在同一個{ }中,也屬於同一個塊,能夠正常訪問到)

    })();  
    // console.log(d); // 報錯  
    // console.log(e); // 報錯  
    // console.log(f); // 報錯  
</script>

 

   考慮到環境致使的行爲差別太大,應該避免在塊級做用域內聲明函數。若是確實須要,也應該寫成函數表達式。
   ES6的塊級做用域容許聲明函數的規則,只在使用大括號的狀況下成立,沒有使用大括號,會報錯。

參考:

http://www.javashuo.com/article/p-bsiyjunf-mr.html

https://blog.csdn.net/hot_cool/article/details/78302673

相關文章
相關標籤/搜索