ES6 - let、const、var的區別

爲了使JavaScript語言能夠用來編寫複雜的大型應用程序,成爲企業級開發語言,ECMAScript 6.0(簡稱ES6)在標準中添加了不少新的特性。咱們將用幾篇文章總結一下ES6標準中一些經常使用的新特性。本片文章主要講解ES6中的letconst命令,並區分其與var命令的區別。同時歡迎你們隨時指正錯誤、探討交流。git

本文已同步至個人我的主頁。歡迎訪問查看更多內容!謝謝你們的關注和支持!github

let 與 var 的區別

1、let聲明的變量只在其所在的塊級做用於有效

所謂塊級做用域是指:將多個代碼語句封裝在一塊兒,一般是包含在一個大括號中,沒有返回值。好比:web

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

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

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

switch (case) {  // 塊級做用域  }
複製代碼

以上例子,大括號({...})中造成的都屬於塊級做用域。數組

衆所周知,在ES6以前,JavaScript中只有全局做用域和局部(函數)做用域,不存在塊級做用域。並且也只能使用關鍵字var來聲明變量。因此用var聲明的變量要麼是屬於全局做用域的全局變量,要麼就是屬於局部(函數)做用域的局部變量。bash

在ES6標準中,添加了使用let聲明變量的方式。使用let聲明的變量只在塊級做用域中有效,在其外層做用域訪問時就會報錯。函數

if (true) {
    // 這個用let聲明的變量a,只在當前塊級做用域中有效
    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'
複製代碼

上面的例子中,由於變量a是使用let聲明的,它只在其所在的塊級做用域——if後面的大括號({...})之中有效,在塊級做用域外層訪問時就會報錯。而用var聲明的變量b,不受塊級做用域的約束,能夠跨塊級做用域訪問。這個例子中,變量b實際是屬於全局做用域的全局變量。ui

那麼,爲何ES6中須要引入塊級做用域的概念呢?爲何要增長使用let來聲明變量的方式呢?spa

由於,若是沒有塊級做用域會致使一些不合理的情形出現。指針

一、 內層變量可能會覆蓋外層變量。code

var a = 'Global';

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

inner();
複製代碼

這個例子,當在函數inner內部if代碼塊內首先訪問變量a時,卻獲得的是undefined。這是由於緊隨其後var聲明的同名變量a會變量提高並覆蓋全局變量a。因此打印出a的值爲undefined

二、計數的循環變量會泄露爲全局變量

for (var i = 0; i < 10; i++) {
    // 一些循環操做
}

console.log(i);     // 10
複製代碼

上面的例子,for循環中的循環變量按道理來講應該只屬於for循環體,循環結束就不能再訪問。但實際這樣用var聲明的i,屬於外層做用域中的變量,也就是說i泄露爲全局變量。因此當執行到console.log(i)時,由於i通過循環已經增長到10,因此打印出i的值爲10

2、let聲明的變量不存在變量提高過程

var聲明的變量,會在其做用域中發生變量提高的過程。變量會被提高到做用域頂部,JS默認給變量一個undefined值。在使用var聲明一個變量前訪問它,獲得的值永遠是undefined

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

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

let a = 'Hello World!'複製代碼

3、let聲明的變量存在「暫時性死區」

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

ES6明確規定,若是區塊中存在letconst命令,這個區塊對這些命令聲明的變量從一開始就造成了封閉做用域。凡是在聲明以前就使用這些變量,就會報錯。

總之,在代碼塊內,使用let命令聲明變量以前,該變量都是不可用的。這在語法上,稱爲「暫時性死區」(temporal dead zone,簡稱 TDZ)。

let g = 'Global';

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

上面的例子中,if代碼塊最頂部一直到let聲明變量g以前,都是g的「暫時性死區」。在該範圍內訪問g都會報錯。

4、let聲明的變量不容許再次重複聲明

使用var聲明變量,能夠屢次重複聲明一個同名變量。最終變量的值爲最後一次聲明賦值的結果。

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

console.log(a);     // 'Hello World!'
複製代碼

可是,在同一做用域(全局/局部/塊級)中不容許使用let重複聲明變量。或者說不容許存在與用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
複製代碼

5、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 的區別

在ES6中,上述全部let所具備的特性,對於const來講一樣存在。但constletvar的區別在於const是用來聲明常量的。

常量具備如下特色:

1、常量值不可修改

一個常量,一旦聲明,任什麼時候間、任何地點都不能修改它的值。

const PI = 3.1415926;

console.log(PI);    // 3.1415926

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

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

不能只聲明一個常量名,但不對其進行初始化賦值。不然在聲明常量時就會報錯。

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

PI = 3.1415926;
複製代碼

3、常量的值不可修改的實質(重要!!)

實際上,常量的值不變,是指常量指向的那個內存地址中所保存的數據不可更改。對於簡單的數據類型(數值,字符串、布爾值),他們自己具體的值就保存在常量所指向的那個內存地址中,因此不能修改改簡單類型的數據值。

可是,若是一個常量的值是一個引用類型值,那麼常量所指向的內存地址中實際保存的是指向該引用類型值的一個指針(也就是引用類型值在內存中的地址)。因此const只能保證該引用類型地址不變,但該地址中的具體數據是能夠變化的。

下面的例子,代碼不會報錯,能夠正常運行!

// !!!常量OBJ中實際保存的是後面的對象在內存中的地址!!!
const OBJ = {};

/**
 * !!!!!!!!!!
 * 修改OBJ.prop1,實際只是修改了對象的屬性,
 * 但並無改變該對象在內存中的地址,
 * 因此常量OBJ並無發生變化
 * !!!!!!!!!!
 */
OBJ.prop1 = 123;
OBJ.prop2 = 'Hello World!'

/**
 * !!!!!!!!!!
 * 下面這一行就會報錯,
 * 由於此時OBJ指向了另外一個對象,OBJ中保存的地址發生了變化
 * !!!!!!!!!!
 */
OBJ = {};   // 報錯——Uncaught TypeError: Assignment to constant variable.
複製代碼

下面的例子和上面同理。

const ARR = [];
ARR.push('Hello');  // 可執行
ARR.length = 0;     // 可執行
ARR = ['Dave'];     // 報錯,由於ARR從新指向了數組['Dave']所在的內存地址
複製代碼
相關文章
相關標籤/搜索