ES6 - let、const、var的區(qū)別

為了使JavaScript語言可以用來編寫復(fù)雜的大型應(yīng)用程序,成為企業(yè)級開發(fā)語言,ECMAScript 6.0(簡稱ES6)在標(biāo)準(zhǔn)中添加了很多新的特性。我們將用幾篇文章總結(jié)一下ES6標(biāo)準(zhǔn)中一些常用的新特性。本片文章主要講解ES6中的let、const命令,并區(qū)分其與var命令的區(qū)別。同時歡迎大家隨時指正錯誤、探討交流。

本文已同步至我的個人主頁。歡迎訪問查看更多內(nèi)容!謝謝大家的關(guān)注和支持!

let 與 var 的區(qū)別

一、let聲明的變量只在其所在的塊級作用于有效

所謂塊級作用域是指:將多個代碼語句封裝在一起,通常是包含在一個大括號中,沒有返回值。比如:

if (true) {  // 塊級作用域  }

for (let i = 0; i < 10; i++) {  // 塊級作用域  }

while (true) {  // 塊級作用域  }

switch (case) {  // 塊級作用域  }

以上例子,大括號({...})中形成的都屬于塊級作用域。

眾所周知,在ES6之前,JavaScript中只有全局作用域和局部(函數(shù))作用域,不存在塊級作用域。而且也只能使用關(guān)鍵字var來聲明變量。所以用var聲明的變量要么是屬于全局作用域的全局變量,要么就是屬于局部(函數(shù))作用域的局部變量。

在ES6標(biāo)準(zhǔn)中,添加了使用let聲明變量的方式。使用let聲明的變量只在塊級作用域中有效,在其外層作用域訪問時就會報錯。

if (true) {
    // 這個用let聲明的變量a,只在當(dāng)前塊級作用域中有效
    let a = 123;
    // 這個用var聲明的變量b,在全局作用域中都有效
    var b = '123';

    console.log(a);     // 123
    console.log(b);     // '123'
}

console.log(a);     // 報錯 —— ReferenceError: a is not defined.
console.log(b);     // '123'

上面的例子中,因?yàn)樽兞?code>a是使用let聲明的,它只在其所在的塊級作用域——if后面的大括號({...})之中有效,在塊級作用域外層訪問時就會報錯。而用var聲明的變量b,不受塊級作用域的約束,可以跨塊級作用域訪問。這個例子中,變量b實(shí)際是屬于全局作用域的全局變量。

那么,為什么ES6中需要引入塊級作用域的概念呢?為什么要增加使用let來聲明變量的方式呢?

因?yàn)?,如果沒有塊級作用域會導(dǎo)致一些不合理的情形出現(xiàn)。

1、 內(nèi)層變量可能會覆蓋外層變量。

var a = 'Global';

function inner() {
    if (true) {
        console.log(a);     // undefined
        var a = 'inner';
        /**
         * 以上兩行代碼相當(dāng)于
         * var a;
         * console.log(a);
         * a = 'inner'; 
         * 再次使用var聲明同名變量a,會覆蓋全局變量a
         */
    }
}

inner();

這個例子,當(dāng)在函數(shù)inner內(nèi)部if代碼塊內(nèi)首先訪問變量a時,卻得到的是undefined。這是因?yàn)榫o隨其后var聲明的同名變量a會變量提升并覆蓋全局變量a。所以打印出a的值為undefined。

2、計數(shù)的循環(huán)變量會泄露為全局變量

for (var i = 0; i < 10; i++) {
    // 一些循環(huán)操作
}

console.log(i);     // 10

上面的例子,for循環(huán)中的循環(huán)變量按道理來說應(yīng)該只屬于for循環(huán)體,循環(huán)結(jié)束就不能再訪問。但實(shí)際這樣用var聲明的i,屬于外層作用域中的變量,也就是說i泄露為全局變量。所以當(dāng)執(zhí)行到console.log(i)時,因?yàn)?code>i經(jīng)過循環(huán)已經(jīng)增加到10,所以打印出i的值為10。

二、let聲明的變量不存在變量提升過程

var聲明的變量,會在其作用域中發(fā)生變量提升的過程。變量會被提升到作用域頂部,JS默認(rèn)給變量一個undefined值。在使用var聲明一個變量前訪問它,得到的值永遠(yuǎn)是undefined。

但是,在ES6中使用let聲明的變量,不存在變量提升過程。也就是說,不能在使用let聲明任何一個變量前訪問它,否則都會報錯。

console.log(a);     // 報錯——ReferenceError: a is not defined

let a = 'Hello World!';

三、let聲明的變量存在“暫時性死區(qū)”

只要使用let聲明了一個變量,那這個變量就“綁定”到了這個作用域(全局/局部/塊級),該變量就不再受外層作用域的影響。

ES6明確規(guī)定,如果區(qū)塊中存在letconst命令,這個區(qū)塊對這些命令聲明的變量從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。

總之,在代碼塊內(nèi),使用let命令聲明變量之前,該變量都是不可用的。這在語法上,稱為“暫時性死區(qū)”(temporal dead zone,簡稱 TDZ)。

let g = 'Global';

if (true) {
    g = 'Block';    // 報錯——ReferenceError: g is not defined
    let g;
}

上面的例子中,if代碼塊最頂部一直到let聲明變量g之前,都是g的“暫時性死區(qū)”。在該范圍內(nèi)訪問g都會報錯。

四、let聲明的變量不允許再次重復(fù)聲明

使用var聲明變量,可以多次重復(fù)聲明一個同名變量。最終變量的值為最后一次聲明賦值的結(jié)果。

var a = 123;
var a = 'Hello World!';

console.log(a);     // 'Hello World!'

但是,在同一作用域(全局/局部/塊級)中不允許使用let重復(fù)聲明變量?;蛘哒f不允許存在與用let聲明的變量同名的變量。以下代碼都會報錯!

// 先var,后let
var a = 123;
// ...一些代碼
let a = 'Hello World!';     // 報錯——Uncaught SyntaxError: Identifier 'a' has already been declared

// 先let,后var
let b = 123;
// ...一些代碼
var b = 'Hello World!';     // 報錯——Uncaught SyntaxError: Identifier 'a' has already been declared

// 先let,再let
let c = 123;
// ...一些代碼
let c = 'Hello World!';     // 報錯——Uncaught SyntaxError: Identifier 'a' has already been declared

五、let聲明的全局變量不會作為window對象的一個屬性

使用var聲明的全局變量,會被JS自動添加在全局對象window上,作為該對象的一個屬性。

var myVar = 'myName';

console.log(window.myVar);      // 'myName'
console.log(window.hasOwnProperty('myVar'));    // true

但是,使用let聲明的全局變量不會作為window對象的一個屬性。

let yourVar = 'yourName';

console.log(window.yourVar);      // undefined
console.log(window.hasOwnProperty('yourVar'));    // false

這個例子可以看出,let聲明的全局變量yourVar,并沒有被添加到window對象上,沒有作為window的一個屬性。

let 與const 的區(qū)別

在ES6中,上述所有let所具有的特性,對于const來說同樣存在。但constletvar的區(qū)別在于const是用來聲明常量的。

常量具有以下特點(diǎn):

一、常量值不可修改

一個常量,一旦聲明,任何時間、任何地點(diǎn)都不能修改它的值。

const PI = 3.1415926;

console.log(PI);    // 3.1415926

PI = 3; // 報錯——Uncaught TypeError: Assignment to constant variable.

二、常量在聲明時必須必須立即初始化(賦初始值)

不能只聲明一個常量名,但不對其進(jìn)行初始化賦值。否則在聲明常量時就會報錯。

const PI;   // 報錯——Uncaught SyntaxError: Missing initializer in const declaration

PI = 3.1415926;

三、常量的值不可修改的實(shí)質(zhì)(重要?。。?/h4>

實(shí)際上,常量的值不變,是指常量指向的那個內(nèi)存地址中所保存的數(shù)據(jù)不可更改。對于簡單的數(shù)據(jù)類型(數(shù)值,字符串、布爾值),他們本身具體的值就保存在常量所指向的那個內(nèi)存地址中,所以不能修改改簡單類型的數(shù)據(jù)值。

但是,如果一個常量的值是一個引用類型值,那么常量所指向的內(nèi)存地址中實(shí)際保存的是指向該引用類型值的一個指針(也就是引用類型值在內(nèi)存中的地址)。所以const只能保證該引用類型地址不變,但該地址中的具體數(shù)據(jù)是可以變化的。

下面的例子,代碼不會報錯,可以正常運(yùn)行!

// !!!常量OBJ中實(shí)際保存的是后面的對象在內(nèi)存中的地址!!!
const OBJ = {};

/**
 * !!!!!!!!!!
 * 修改OBJ.prop1,實(shí)際只是修改了對象的屬性,
 * 但并沒有改變該對象在內(nèi)存中的地址,
 * 所以常量OBJ并沒有發(fā)生變化
 * !!!!!!!!!!
 */
OBJ.prop1 = 123;
OBJ.prop2 = 'Hello World!'

/**
 * !!!!!!!!!!
 * 下面這一行就會報錯,
 * 因?yàn)榇藭rOBJ指向了另一個對象,OBJ中保存的地址發(fā)生了變化
 * !!!!!!!!!!
 */
OBJ = {};   // 報錯——Uncaught TypeError: Assignment to constant variable.

下面的例子和上面同理。

const ARR = [];
ARR.push('Hello');  // 可執(zhí)行
ARR.length = 0;     // 可執(zhí)行
ARR = ['Dave'];     // 報錯,因?yàn)锳RR重新指向了數(shù)組['Dave']所在的內(nèi)存地址

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

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

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