es6入門1-- let與var的區別詳解


一、前言

說到做到,現在暫時放了放JS模式的讀書筆記,打算好好看看ES6,畢竟出了這么久了,還是靠JS吃飯的,都不好好學JS新特性,確實說不過去,我本來是想當讀書筆記去記錄ES6,但是這個確實是屬於邊看邊用邊記憶的,所以還是零散的挑重點去記錄吧。

二、let與var的區別

1.let 不能重復聲明,但var可以

這是最容易記憶的一點,當我們使用var聲明時,是可以反復聲明同一個變量的,但並不推薦這個做,因為同名變量的創建對於內存使用是無意義的,所以let的出現也是為了解決早期設計缺陷。
var a = 1;
var a = 2;//不會報錯
let b = 1;
let b = 2;//報錯

2.let會產生塊級作用域,且只在自己的作用域內生效,但var不受限制

ES5 只有全局作用域和函數作用域,那什么是塊級作用域?當我們在{}中使用了let或者const時,{}的范圍就是一個塊級作用域,此時let或const只能在{}中訪問,像這樣:

{
  let a = 1;
  var b = 2;
}
console.log(a);//報錯
console.log(a);//2

或者這樣:

if(true){
  let a = 1;
  var b = 1;
}
console.log(a);//報錯
console.log(b);//1
特別注意,let與var在for循環中表現不同
//for循環用var
var a = [];
for (var i = 0; i < 5; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[1]();//5
a[2]();//5
a[3]();//5
//for循環用let
var b = [];
for (let i = 0; i < 5; i++) {
  b[i] = function () {
    console.log(i);
  };
}
b[1]();//1
b[2]();//2
b[3]();//3

在明白上面2個循環的區別之前,我們先來理一理知識點,我們要知道,for循環中設置循環變量的部位其實是一個父作用域,循環體內部是個子作用域。

在父子作用域中使用let聲明同一變量時,對於此變量而言,兩個作用域的同名變量其實是互不相關的兩個獨立變量:

{
  let a = 1;
  {
    console.log(a);//報錯,暫時性死域,在let a前面使用a不合法
    let a = 2;
    console.log(a);//2
  }
  console.log(a);//1
}

即便父子作用域都使用了let,子作用域也能正常繼承自己非let的變量,父也能讀取子作用域非let的變量。

{
  let a = 1;
  {
  //這里使用了let,也是塊級作用域,但也能正常訪問父塊級作用域的變量a
  let c = 1;
var b = 2; console.log(a);//1 } console.log(a);//1 console.log(b);//2 }

所以下面這個循環輸出了三次echo,因為在子作用域中聲明的i跟父作用域中的i可以說是完全不同的兩個i;

for(let i = 0;i<3;i++){
  let i = 'echo';
  console.log(i);
};//輸出三次echo

但如果你將let改為var,你會發現只輸出一次,因為沒有了塊級作用域,父子作用域共用了一個變量i,第一次循環后,子作用域的i被改為了echo,父作用域中i<3的判斷無法通過,所以只輸出了一次。

那么回頭再看看最初的兩個循環,為什么var聲明輸出了3個5,而let輸出了1,2,3呢,嘗試去理解,說到底還是塊級作用域搞的鬼。

3.let不存在變量提升

變量提升可以說是var聲明設計不嚴謹的一點,可能我們在開發中已經習慣聲明一個全局變量,然后可以在聲明前后都可以使用它,這很方便,但是不符合規范。
console.log(a);//undefined
var a = 1;
console.log(b);//報錯
let b = 1;
變量提升甚至會違背你代碼設計的初衷,達到你不想要的結果。
var a = 1;
function f() {
  console.log(a);//undefined
  if (false) {
    var a = 2;
  }
}
f();
你肯定在想,這個地方應該輸出1啊,下面的條件判斷是false,都沒執行,很遺憾,變量提升是聲明提前,賦值在原地,條件判斷管的是要不要給a賦值,聲明早就提前了。這段代碼等同於:
var a = 1;
function f() {
  var a;
  console.log(a);//undefined
  if (false) {
    a = 2;
  }
}
f();

就問你,驚不驚喜?意不意外?

4.let存在暫時性死域

當一個區域存在let聲明時,這個區域就形成了一個封閉的作用域,在let聲明前使用這個變量就會報錯,也就是只能先聲明再使用,這種語法也稱為暫時性死域。
{
  let a = 1;
  {
    console.log(a);//報錯
    let a =2;
  }
}

三、const聲明的特點

可以理解為,let的特性const都有,不能反復聲明,存在塊級作用域,不存在變量提升,也有暫時性死域的特點。

與let區別在於,const聲明的是一個常量,一旦聲明就無法修改,有個誤區需要特別注意,舉個例子:

const a = {};
a.b = 1;
console.log(a);//{b:1}

上述代碼中我聲明了一個常量對象a,但是a對象修改了,並沒有報錯,這是為什么呢?從本質上理解,const聲明的變量並非變量的值不能修改,而是可以理解為變量的指向不能修改。

只是基本類型的數據,鍵值都在棧內存中,但對於復雜數據類型,變量指向的其實是堆內存中存放值所提供的一個地址。所以我們不能修改復雜數據類型的指向,這樣就會報錯:

const a = {};
a.b = 1;
console.log(a);//{b:1}
a = [];//這里直接修改了變量a的指向,不允許了。
console.log(a);//報錯

四、小總結

let與const的出現對於JS有什么影響或者說好處呢?

我們知道,在ES5環境中只有全局作用域與函數作用域,這也導致我們通過var聲明的全局變量直接與全局對象掛鈎,而瀏覽器環境全局對象是window,所以我們隨便聲明的全局對象都可以通過window訪問,很明顯,這樣的環境很容易造成混亂。

var a = 1;
console.log(window.a)//1

而ES6帶來的let與const,可以說是宣告天下,從此我全局變量將逐步與頂層對象屬性脫離開來,還你一片極樂凈土。

當然,ES6中var與function聲明的全局變量還是會出現在頂層變量中,但至少現在提供了一種解決方式,JS語言也在成長的越來越好

番外:

在記錄完let與var在for循環中的不同變現,我覺得我解釋的不夠合理,還是得單獨拿出來說下,即使let與var只是做聲明這件小事,我在拆分for循環步驟時,發現了一個很有趣的問題,所以深挖了一下。有興趣可以閱讀我的這篇博客:

for循環中let var的區別,塊級作用域到底造成了什么影響


免責聲明!

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



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