總結下var、let 和 const 的區別


一、var變量

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>var</title>
    <script>
        window.onload = function(){
            var aLi = document.getElementsByTagName('li');
            for (var i=0;i<aLi.length;i++){  /*將var改為let*/
              aLi[i].onclick = function(){
              alert(i);    /*單擊任何標簽都輸出4*/
        }
    }
}
    </script>
</head>
<body>
    <ul>
        <li>0</li>
        <li>1</li>
        <li>2</li>
        <li>3</li>
</ul>
</body>
</html>

二、let變量

ES5 只有全局作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景,在ES6之前,大部分人會選擇使用閉包來解決這個問題,今天我們使用ES6提供的let來解決這個問題。

代碼大同小異,只需將上例子代碼for循環中的var改為let,即可實現的效果是點擊不同的<li>標簽,alert出其對應的索引值。

 

window.onload = function(){
    var aLi = document.getElementsByTagName('li');
    for (let i=0;i<aLi.length;i++){
        aLi[i].onclick = function(){
            alert(i);
        }
    };     
}

 

let 關鍵字可以將變量綁定到所在的任意作用域中(通常是 { .. } 內部)。換句話說,let為其聲明的變量隱式地了所在的塊作用域。  

就是 for循環還有一個特別之處,就是循環語句部分是一個父作用域,而循環體內部是一個單獨的子作用域。

 

var和let的區別

1.函數作用域 vs 塊級作用域

var 和 let 第一點不同就是 let 是塊作用域,即其在整個大括號 {} 之內可見。如果使用 let 來重寫上面的 for 循環的話,會報錯

var:只有全局作用域和函數作用域概念,沒有塊級作用域的概念。但是會把{}內也假稱為塊作用域。

let:只有塊級作用域的概念 ,由 { } 包括起來,if語句和for語句里面的{ }也屬於塊級作用域。

 

/*for循環,for循環里面是父級作用域,循環體內是另一個*/
for( let i = 0 ; i < 3 ; i++ ){
    let i = 'abc'    //用var替代let會報錯提示已經定義,若沒有任何關鍵字則每次賦值給i,最后只會輸出一次abc
    console.log(i)    // 輸出3次abc  
  }

 

 

 

 

      

      

   

function varTest() {
  var x = 31;
  if (true) {
    var x = 71;  // same variable!
    console.log(x);  // 71
  }
  console.log(x);  // 71
}

function letTest() {
  let x = 31;
  if (true) {
    let x = 71;  // different variable
    console.log(x);  // 71
  }
  console.log(x);  // 31
}

2.變量提升 vs 暫時性死區

   let 和 var 的第二點不同是,在變量聲明之前就訪問變量的話,會直接提示 ReferenceError,而不像 var 那樣使用默認值 undefined:

    var 存在變量提升,而 let,const(后面會提及)聲明的變量卻不存在變量提升,所以用 let 定義的變量一定要在聲明后再使用,否則會報錯。

<script>
        /*1.var變量*/
        console.log(a);  //undefined
        var a=1;
     b=10; 
        console.log(b);  //10
        var b;
        
        /*2.let變量*/
        console.log(c); // Uncaught ReferenceError: c is not defined
        let c=2;
        console.log(d); // Uncaught ReferenceError: d is not defined
        let d;
</script>

<script>
var x = 5; // 初始化 x
elem = document.getElementById("demo"); // 查找元素 
elem.innerHTML = "x 為:" + x + ",y 為:" + y;           // 顯示 x 和 y
var y = 7; // 初始化 y
</script>

結果輸出: x 為:5,y 為:undefined

y 輸出了 undefined,這是因為變量聲明 (var y) 提升了,但是初始化(y = 7) 並不會提升,所以 y 變量是一個未定義的變量。
<script>
a=5;
show();
var a;
function show(){};
預解析:

function show(){};
var a;
a=5;
show();  //需要注意都是函數聲明提升直接把整個函數提到執行環境的最頂端。
</script>

 

 

 

 

 

 

 可以看出,雖然代碼中console調用a在前,聲明a在后,但是由於在js中,函數及變量的聲明都將被提升到函數的最頂部,也就是說(var聲明的)變量可以先使用再聲明。

 

ES6明確規定,如果區塊中存在let命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。所以在代碼塊內,使用let命令聲明變量之前,該變量都是不可用的。這在語法上,稱為“暫時性死區”(temporal dead zone,簡稱 TDZ)。

let a = 'outside';
if(true) {
   console.log(a);//Uncaught ReferenceError: a is not defined
    let a = "inside";
}

當前作用域頂部到該變量聲明位置中間的部分,都是該let變量的死區,在死區中,禁止訪問該變量。由此,我們給出結論let聲明的變量存在變量提升, 但是由於死區我們無法在聲明前訪問這個變量

 

“暫時性死區”也意味着typeof不再是一個百分之百安全的操作,因為會使typeof報錯。 

{
typeof name;//ReferenceError
let name;
}

只要塊級作用域內存在let命令,它所聲明的變量就“綁定”(binding)這個區域,不再受外部的影響。在代碼塊中,使用let命令聲明變量之前,該變量都是不可用的,這在語法上稱為“暫時性死亡”。

 

3.let不允許重復聲明變量

可以看出,var:變量可以多次聲明,而let不允許在相同作用域內,重復聲明同一個變量。

<script>
    if (true) {
      let a;
      let a; // Uncaught SyntaxError: Identifier 'a' has already been declared
    }

    if(true){
      var d;
      var d;    //不會報錯
    }

    if (true) {
      var c;
      let c; // Uncaught SyntaxError: Identifier 'c' has already been declared
    }

    if (true) {
      let d;
      var d; // Uncaught SyntaxError: Identifier 'd' has already been declared
    }
</script>

4.全局變量vs全局對象的屬性

ES5中全局對象的屬性與全局變量基本是等價的,但是也有區別,比如通過var聲明的全局變量不能使用delete從 window/global ( global是針對與node環境)上刪除,不過在變量的訪問上基本等價。

ES6 中做了嚴格的區分,使用 var 和 function 聲明的全局變量依舊作為全局對象的屬性,使用 letconst 命令聲明的全局變量不屬於全局對象的屬性。

 

<script>
    var a = 10;
    console.log(window.a); //10
    console.log(this.a) //10

    let b = 20;
    console.log(window.b); // undefined
    console.log(this.b) // undefined
</script>

 

三、const聲明的常量

除了let以外,ES6還引入了cons,const 和 let 的作用域是一致的,不同的是 const 變量一旦被賦值,就不能再改變了,但是這並不意味着使用 const 聲明的變量本身不可變,只是說它不可被再次賦值了,而且const 聲明的變量必須經過初始化。

const a = 1;

a = 2; // // Uncaught TypeError: Assignment to constant variable
const b; // Uncaught SyntaxError: Missing initializer in const declaration

 

注:復合類型const變量保存的是引用。因為復合類型(如數組和對象)的常量不指向數據,而是指向數據(heap)所在的地址(stack),所以通過 const 聲明的復合類型只能保證其地址引用不變,但不能保證其數據不變。所以將一個對象聲明為常量必須非常小心。

簡單數據類型(數值,字符串,布爾值):值保存在變量指向的那個內存地址,因此等同於常量。

復合類型的數據(對象和數組):變量指向的是內存地址,保存的是一個指針,const只能保存這個指針地址是固定的,至於他指向的數據結構是不是可變的,就完全不能控制了。

 

<script>
    /*不會報錯,因為names1指向的地址不變,改變的只是內部數據*/
    const names1 = [];
          names1[0] = 1;
          names1[1] = 2;
          names1[2] = 3;
          names1[3] = 10;
    console.log(names1);

    /*出錯,因為變量names2指向的地址不能發生改變,應始終指向[]所在的地址,[1,4]與[6,7]不是同一個地址*/
    const names2=[1,4];
          names2=[6,7];   //報錯
</script>

 最后

但是什么時候用 var、let 或 const 呢?我的建議是,大多數情況下都使用 const,除非你知道你的變量的值還會被改變,以上大概是總結后的內容,看來,還是多用 let 、const 吧。

 

部分參考資料來源:https://www.cnblogs.com/slly/p/9234797.html

 

 

 


免責聲明!

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



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