var、let、const區(qū)別

一、var聲明的變量會(huì)掛載在window上,而let和const聲明的變量不會(huì)

var a =100;

console.log(a,window.a); // 100 100

let b =10;

console.log(b,window.b);// 10 undefined

const c =1;

console.log(c,window.c);// 1 undefined

二、var聲明變量存在變量提升,let和const不存在變量提升

console.log(a);// undefined? ===>? a已聲明還沒賦值,默認(rèn)得到undefined值

var a =100;

console.log(b);// 報(bào)錯(cuò):b is not defined? ===> 找不到b這個(gè)變量

let b =10;

console.log(c);// 報(bào)錯(cuò):c is not defined? ===> 找不到c這個(gè)變量

const c =10;

三、let和const聲明形成塊作用域,而var不存在此作用域

if(true){

var a =100;

letb =10;

const c =1;

}

console.log(a);// 100

console.log(b)// 報(bào)錯(cuò):b is not defined? ===> 找不到b這個(gè)變量

console.log(c)// 報(bào)錯(cuò):c is not defined? ===> 找不到c這個(gè)變量

注:var聲明的變量屬于全局變量,可以修改,可以聲明相同名字的變量,let、const不可以,只能在作用域內(nèi)使用

ES6之let(理解閉包)和const命令

  最近做項(xiàng)目的過程中,使用到了ES6,因?yàn)橹昂苌俳佑|,所以使用起來還不夠熟悉。因此購買了阮一峰老師的ES6標(biāo)準(zhǔn)入門,在此感謝阮一峰老師的著作。

  我們知道,ECMAScript 6即ES6是ECMAScript的第五個(gè)版本,因?yàn)樵?015年6月正式發(fā)布,所以又成為ECMAScript2015。ES6的主要目的是為了是JS用于編寫復(fù)雜的大型應(yīng)用程序,成為企業(yè)級的開發(fā)語言。

說明:由于有時(shí)候我們希望得知es6代碼的具體實(shí)現(xiàn)原理或者說希望能夠轉(zhuǎn)化為es5使用,我們可以使用http://babeljs.io/來實(shí)現(xiàn)在線將es6代碼轉(zhuǎn)化為es5代碼。

第一部分:let命令

  一.塊級作用域(重點(diǎn))。

   我們知道,在javascript中只有全局作用域和函數(shù)作用域,并不存在塊級作用域。這樣,在使用時(shí)就會(huì)出現(xiàn)一些問題。 下面我們先來舉例說明let塊級作用域的使用。

   例1:

  代碼如下所示:

? ? ? ? {

? ? ? ? ? ? vara=5;

? ? ? ? ? ? let b=10;

? ? ? ? }

? ? ? ? console.log(a);

? ? ? ? console.log(b);

  我們在控制臺(tái)得到的結(jié)果如下所示:

  也就是說,var聲明的變量由于不存在塊級作用域所以可以在全局環(huán)境中調(diào)用,而let聲明的變量由于存在塊級作用域所以不能在全局環(huán)境中調(diào)用。

 例2:這個(gè)例子是一個(gè)非常經(jīng)典的例子。

vara=[];

? ? ? ? for(vari=0;i<10;i++){

? ? ? ? ? ? a[i]=function(){

? ? ? ? ? ? ? ? console.log(i);

? ? ? ? ? ? };

? ? ? ? }

? ? ? ? a[6]();//10? ?

vara=[];

? ? for(let i=0;i<10;i++){

? ? ? ? a[i]=function(){

? ? ? ? ? ? console.log(i);

? ? ? ? };

? ? }

? ? a[6]();//6? ?


我們可以看到,兩個(gè)例子中,唯一的區(qū)別是前者for循環(huán)中使用var來定義i,得到的結(jié)果是10.而后者使用的是let來定義i,最終得到的結(jié)果是6.這是為什么呢?阮一峰老師在書中的解釋并不是很清楚,所以下面我會(huì)發(fā)表個(gè)人見解:

關(guān)于這個(gè)問題,表面上確實(shí)不是很好理解,查詢了很多資料,許多人講到了很多晦澀難懂的知識,似乎很高大上,但是實(shí)際上并不難,下面根據(jù)我的理解進(jìn)行解釋,如有問題,歡迎批評指正,如果大家能夠有些收獲就再好不過了。



例二前者(var i)具體執(zhí)行過程如下:

var a=[];

var i=0;//由于var來聲明變量i,所以for循環(huán)代碼塊不具備塊級作用域,因此i認(rèn)為是全局變量,直接放在全局變量中。

a[0]=function(){

console.log(i);//這里之所以i為i而不是0;是因?yàn)槲覀冎皇嵌x了該函數(shù),未被調(diào)用,所以沒有進(jìn)入該函數(shù)執(zhí)行環(huán)境,i當(dāng)然不會(huì)沿著作用域鏈向上搜索找到i的值。

}// 由于不具備塊級作用域,所以該函數(shù)定義就是全局作用域。

var i=1;//第二次循環(huán),這時(shí)var i=1;覆蓋了前面的var i=0;即現(xiàn)在i為1;

a[1]=function(){

console.log(i);//解釋同a[0]函數(shù)。

}

var i=2;// 第三次循環(huán),這時(shí) i=2,在全局作用域中,所以覆蓋了前面的i=1;

a[2]=function(){

console.log(i);

}

......第四次循環(huán) 此時(shí)i=3這個(gè)以及下面的i不斷的覆蓋前面的i,因?yàn)槎荚谌肿饔糜蛑?/p>

......第五次循環(huán) 此時(shí)i=4

......第六次循環(huán) 此時(shí)i=5

......第七次循環(huán) 此時(shí)i=6

......第八次循環(huán) 此時(shí)i=7

......第九次循環(huán) 此時(shí)i=8

var i=9;

a[9]=function(){

console.log(i);

}

var i=10;// 這時(shí)i為10,因?yàn)椴粷M足循環(huán)條件,所以停止循環(huán)。

緊接著在全局環(huán)境中繼續(xù)向下執(zhí)行。

a[6]();//這時(shí)調(diào)用a[6]函數(shù),所以這時(shí)隨即進(jìn)入a[6]函數(shù)的執(zhí)行環(huán)境,即a[6]=function(){console.log(i)};執(zhí)行函數(shù)中的代碼 console.log(i); 因?yàn)樵诤瘮?shù)執(zhí)行環(huán)境中不存在變量i,所以此時(shí)會(huì)沿著作用域鏈向上尋找(可參考我的博文《深入理解作用域和作用域鏈》),即進(jìn)入了全局作用域中尋找變量i,而全局作用域中i=10覆蓋了前面所有的i值,所以說這時(shí)i為10,那么a[6]的值就是10了。

  

說明:對于例如a[1]=function(){console.log(i)};而不是a[1]=function{console.log(1)},可以在控制臺(tái)中輸出a[1]函數(shù),即可得到驗(yàn)證。



例二后者(let i)具體執(zhí)行過程如下:


var a=[];//創(chuàng)建一個(gè)數(shù)組a;

{ //進(jìn)入第一次循環(huán)

let i=0; //注意:因?yàn)槭褂胠et使得for循環(huán)為塊級作用域,此次let i=0在這個(gè)塊級作用域中,而不是在全局環(huán)境中。

a[0]=function(){

console.log(i);

}; //注意:由于循環(huán)時(shí),let聲明i,所以整個(gè)塊是塊級作用域,那么a[0]這個(gè)函數(shù)就成了一個(gè)閉包。

}//?聲明:?我這里用{}表達(dá)并不符合語法,只是希望通過它來說明let存在時(shí),這個(gè)for循環(huán)塊是塊級作用域,而不是全局作用域。


講道理,上面這是一個(gè)塊級作用域,就像函數(shù)作用域一樣,函數(shù)執(zhí)行完畢,其中的變量會(huì)被銷毀,但是因?yàn)檫@個(gè)代碼塊中存在一個(gè)閉包,閉包的作用域鏈中包含著(或著說是引用著)塊級作用域,所以在閉包被調(diào)用之前,這個(gè)塊級作用域內(nèi)部的變量不會(huì)被銷毀。(更多閉包知識,可以看我的博文《JavaScript之閉包》)

{ //進(jìn)入第二次循環(huán)

let i=1; //注意:因?yàn)閘et i=1; 和?上面的let i=0;出在不同的作用域中,所以兩者不會(huì)相互影響。

a[1]=function(){

console.log(i);

}; //同樣,這個(gè)a[i]也是一個(gè)閉包

}

......進(jìn)入第三次循環(huán),此時(shí)其中l(wèi)et i=2;

......進(jìn)入第四次循環(huán),此時(shí)其中l(wèi)et i=3;

......進(jìn)入第五次循環(huán),此時(shí)其中l(wèi)et i=4;

......進(jìn)入第六次循環(huán),此時(shí)其中l(wèi)et i=5;

......進(jìn)入第七次循環(huán),此時(shí)其中l(wèi)et i=6;

......進(jìn)入第八次循環(huán),此時(shí)其中l(wèi)et i=7;

......進(jìn)入第九次循環(huán),此時(shí)其中l(wèi)et i=8;

{//進(jìn)入第十次循環(huán)

let i=9;

a[i]=function(){

console.log(i);

};//同樣,這個(gè)a[i]也是一個(gè)閉包

}

{

let i=10;//不符合條件,不再向下執(zhí)行。于是這個(gè)代碼塊中不存在閉包,let i=10;在這次循環(huán)結(jié)束之后難逃厄運(yùn),隨即被銷毀。

}

a[6]();//調(diào)用a[6]()函數(shù),這時(shí)執(zhí)行環(huán)境隨即進(jìn)入下面這個(gè)代碼塊中的執(zhí)行環(huán)境:funcion(){console.log(i)};

{?

let i=6;?

a[6]=function(){

console.log(i);

}; //同樣,這個(gè)a[i]也是一個(gè)閉包

}

? ? a[6]函數(shù)(閉包)這個(gè)執(zhí)行環(huán)境中,它會(huì)首先尋找該執(zhí)行環(huán)境中是否存在 i,沒有找到,就沿著作用域鏈繼續(xù)向上到了其所在的代碼塊執(zhí)行環(huán)境,找到了i=6,于是輸出了6,即a[6]();的結(jié)果為6。這時(shí),閉包被調(diào)用,所以整個(gè)代碼塊中的變量i和函數(shù)a[6]()被銷毀。


相信大家仔細(xì)看完上面的函數(shù)執(zhí)行的過程,對let var 塊級作用域 閉包就有一個(gè)很好的理解了。我認(rèn)為重要的是對于函數(shù)執(zhí)行過程的理解!



二.不存在變量提升

這里是說使用let不會(huì)像使用var一樣存在一個(gè)變量提升的現(xiàn)象。變量提升是什么呢?在沒有接觸es6之前我對此也不清楚,但是我想大家一定都聽說過函數(shù)聲明提升:函數(shù)聲明來定義函數(shù)即可實(shí)現(xiàn)函數(shù)聲明提升,這樣,我們可以先調(diào)用函數(shù),后聲明函數(shù);而函數(shù)表達(dá)式方法不會(huì)實(shí)現(xiàn)函數(shù)聲明提升,這樣,如果先調(diào)用函數(shù),后聲明函數(shù),則會(huì)拋出錯(cuò)誤!?。?/b>對于函數(shù)聲明提升更多知識可以看我的博文《JavaScript函數(shù)之美~》)。那么可以以此類推,var定義變量:可以先使用,后聲明;而let定義變量:只可先聲明,后使用。

例3:

  

varnum1=100;

? ? ? ? console.log(num1);

? ? ? ? let num2=200;

? ? ? ? console.log(num2);

? ? ? ? console.log(i);

? ? ? ? vari=10;

? ? ? ? console.log(j);

? ? ? ? let j=5;

    我們可以看到結(jié)果如下:

即前兩個(gè)都是先聲明后使用,沒有問題。而后兩個(gè)都是先使用,后聲明,用var 聲明的顯示undefined,而 let聲明的直接報(bào)錯(cuò)。

說明:console.log(i);

   var i=10;

實(shí)際上相當(dāng)于:

   ?var i;

   ?console.log(i);

   i=10;

所以會(huì)出現(xiàn)undefined的情況。


三.暫時(shí)性死區(qū)

  暫時(shí)性死區(qū)即:只要一進(jìn)入當(dāng)前作用域,所要使用的變量就已經(jīng)存在,但是不可獲取,只有等到聲明變量的那一行代碼出現(xiàn),才可以獲取和使用該變量。

例5:


vartmp=123;

? ? if(true){

? ? ? ? tmp="abc";

? ? ? ? let tmp;

? ? }


? ?結(jié)果如下:

也就是說:雖然上面的代碼中存在全局變量tmp,但是塊級作用域內(nèi)let又聲明了一個(gè)局部變量tmp,導(dǎo)致后者綁定了塊級作用域,所以在let聲明變量前,對tmp賦值會(huì)報(bào)錯(cuò)。此即暫時(shí)性死區(qū)。

  

  注意:ES6規(guī)定暫時(shí)性死區(qū)和不存在變量提升就是為了減少運(yùn)行時(shí)的錯(cuò)誤,防止在變量聲明前就使用這個(gè)變量,從而導(dǎo)致意料之外的行為。

  

  暫時(shí)性死區(qū)就是: 只要塊級作用域內(nèi)存在let,那么他所聲明的變量就綁定了這個(gè)區(qū)域,不再受外部的影響。

  暫時(shí)性死區(qū)即 Temperary Dead Zone,即TDZ。?

  

? ? ? ?注意:暫時(shí)性死區(qū)也意味著 typeof 不再是一個(gè)百分之百安全的操作。 ?如下:

if(true) {

? ? ? console.log(typeof x);

? ? ? let x;

? ? }

  這里如果沒有l(wèi)et x,那么typeof x的結(jié)果是 undefined,但是如果使用了let x,因?yàn)閘et不存在變量提升,所以這里形成了暫時(shí)性死區(qū),即typeof x也是會(huì)報(bào)錯(cuò)的。。。 ?從這里可以理解暫時(shí)性死區(qū)實(shí)際上就是這一部分是有問題的 。




四.不允許重復(fù)聲明

  

function func (){

? ? ? ? let b=100;

? ? ? ? varb=10;

? ? }

? ? function add(num){

? ? ? ? let num;

? ? ? ? returnnum+1;

? ? }

? ? function another(){

? ? ? ? let a=10;

? ? ? ? let a=5;

? ? }

上述三個(gè)得到的結(jié)果均為:

只是前兩者為 b和num被聲明過了。注意:第二個(gè)函數(shù),雖然我們沒有明確的聲明,但是參數(shù)實(shí)際上是相當(dāng)于用var聲明的局部變量。




第二部分:const命令

什么使const命令呢?實(shí)際上它也是一種聲明常量的方式。const命令用來聲明常量,一旦聲明,其值就不能改變。初次之外,const和let十分相似。也就是說前者是用于聲明常量的,后者是用于聲明變量的。


    1.const聲明常量,一旦聲明,不可改變。

const a=10;

? ? a=100;

    結(jié)果如下

    2.既然const一旦聲明不可改變,所以在聲明時(shí)必須初始化。

const a;

     結(jié)果如下:


    3.const所在的代碼塊為塊級作用域,所以其變量只在塊級作用域內(nèi)使用或其中的閉包使用。

if(true){

? ? ? ? const a=10;

? ? }

? ? console.log(a);

    結(jié)果如下:


    4.const聲明的變量不存在變量提升。

if(true){

? ? ? ? console.log(a);

? ? ? ? const a=10;

? ? }

結(jié)果如下:


    5.const不可重復(fù)聲明常量。

vara=10;

? ? const a=5;

結(jié)果如下:

need-to-insert-img



6.const命令只是保證了變量名指向的地址不變,并不保證該地址的數(shù)據(jù)不變。

  

need-to-insert-img

const a={};

? ? a.name="zzw";

? ? console.log(a.name);

? ? const b=[];

? ? b.push("zzw");

? ? console.log(b);

? ? const c={};

? ? c={name:"zzw"};

need-to-insert-img

  結(jié)果如下:

need-to-insert-img

因此,我們使用const所指向的地址不可變,但是地址的內(nèi)容是可以變得。


    7.如果希望將對象本身凍結(jié),可以使用Object.freeze()方法。

const a=Object.freeze({});

? ? a.name="zzw";

? ? console.log(a.name); //undefined

    于是通過Object.freeze()方法我們就不可以再改變對象的屬性了(無效)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容