為了使JavaScript語言可以用來編寫復雜的大型應用程序斯辰,成為企業(yè)級開發(fā)語言舶担,
ECMAScript 6.0
(簡稱ES6
)在標準中添加了很多新的特性。我們將用幾篇文章總結一下ES6標準中一些常用的新特性彬呻。本片文章主要講解ES6中的let
衣陶、const
命令柄瑰,并區(qū)分其與var
命令的區(qū)別。同時歡迎大家隨時指正錯誤剪况、探討交流教沾。
本文已同步至我的個人主頁。歡迎訪問查看更多內容译断!謝謝大家的關注和支持授翻!
let 與 var 的區(qū)別
一、let聲明的變量只在其所在的塊級作用于有效
所謂塊級作用域是指:將多個代碼語句封裝在一起孙咪,通常是包含在一個大括號中堪唐,沒有返回值。比如:
if (true) { // 塊級作用域 }
for (let i = 0; i < 10; i++) { // 塊級作用域 }
while (true) { // 塊級作用域 }
switch (case) { // 塊級作用域 }
以上例子翎蹈,大括號({...})中形成的都屬于塊級作用域淮菠。
眾所周知,在ES6之前荤堪,JavaScript中只有全局作用域和局部(函數)作用域合陵,不存在塊級作用域。而且也只能使用關鍵字var
來聲明變量澄阳。所以用var
聲明的變量要么是屬于全局作用域的全局變量拥知,要么就是屬于局部(函數)作用域的局部變量。
在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
實際是屬于全局作用域的全局變量。
那么恋谭,為什么ES6中需要引入塊級作用域的概念呢糠睡?為什么要增加使用let
來聲明變量的方式呢?
因為疚颊,如果沒有塊級作用域會導致一些不合理的情形出現狈孔。
1信认、 內層變量可能會覆蓋外層變量。
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
。
2深寥、計數的循環(huán)變量會泄露為全局變量
for (var i = 0; i < 10; i++) {
// 一些循環(huán)操作
}
console.log(i); // 10
上面的例子护蝶,for
循環(huán)中的循環(huán)變量按道理來說應該只屬于for
循環(huán)體,循環(huán)結束就不能再訪問翩迈。但實際這樣用var
聲明的i
持灰,屬于外層作用域中的變量,也就是說i
泄露為全局變量负饲。所以當執(zhí)行到console.log(i)
時堤魁,因為i
經過循環(huán)已經增加到10
,所以打印出i
的值為10
返十。
二妥泉、let聲明的變量不存在變量提升過程
用var
聲明的變量,會在其作用域中發(fā)生變量提升
的過程洞坑。變量會被提升到作用域頂部盲链,JS默認給變量一個undefined
值。在使用var
聲明一個變量前訪問它迟杂,得到的值永遠是undefined
刽沾。
但是,在ES6中使用let
聲明的變量排拷,不存在變量提升
過程侧漓。也就是說,不能在使用let
聲明任何一個變量前訪問它监氢,否則都會報錯布蔗。
console.log(a); // 報錯——ReferenceError: a is not defined
let a = 'Hello World!';
三浪腐、let聲明的變量存在“暫時性死區(qū)”
只要使用let
聲明了一個變量纵揍,那這個變量就“綁定”到了這個作用域(全局/局部/塊級),該變量就不再受外層作用域的影響议街。
ES6明確規(guī)定泽谨,如果區(qū)塊中存在let
和const
命令,這個區(qū)塊對這些命令聲明的變量從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量隔盛,就會報錯。
總之拾稳,在代碼塊內吮炕,使用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ū)”搜骡。在該范圍內訪問g
都會報錯拂盯。
四、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
五、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
來說同樣存在。但const
與let
舔庶、var
的區(qū)別在于const
是用來聲明常量的抛蚁。
常量具有以下特點:
一陈醒、常量值不可修改
一個常量,一旦聲明瞧甩,任何時間钉跷、任何地點都不能修改它的值。
const PI = 3.1415926;
console.log(PI); // 3.1415926
PI = 3; // 報錯——Uncaught TypeError: Assignment to constant variable.
二肚逸、常量在聲明時必須必須立即初始化(賦初始值)
不能只聲明一個常量名爷辙,但不對其進行初始化賦值。否則在聲明常量時就會報錯朦促。
const PI; // 報錯——Uncaught SyntaxError: Missing initializer in const declaration
PI = 3.1415926;
三膝晾、常量的值不可修改的實質(重要!N衩帷)
實際上血当,常量的值不變,是指常量指向的那個內存地址中所保存的數據不可更改禀忆。對于簡單的數據類型(數值歹颓,字符串、布爾值)油湖,他們本身具體的值就保存在常量所指向的那個內存地址中巍扛,所以不能修改改簡單類型的數據值。
但是乏德,如果一個常量的值是一個引用類型值撤奸,那么常量所指向的內存地址中實際保存的是指向該引用類型值的一個指針(也就是引用類型值在內存中的地址)。所以const只能保證該引用類型地址不變喊括,但該地址中的具體數據是可以變化的胧瓜。
下面的例子,代碼不會報錯郑什,可以正常運行府喳!
// !!!常量OBJ中實際保存的是后面的對象在內存中的地址!!!
const OBJ = {};
/**
* !!!!!!!!!!
* 修改OBJ.prop1,實際只是修改了對象的屬性蘑拯,
* 但并沒有改變該對象在內存中的地址钝满,
* 所以常量OBJ并沒有發(fā)生變化
* !!!!!!!!!!
*/
OBJ.prop1 = 123;
OBJ.prop2 = 'Hello World!'
/**
* !!!!!!!!!!
* 下面這一行就會報錯,
* 因為此時OBJ指向了另一個對象,OBJ中保存的地址發(fā)生了變化
* !!!!!!!!!!
*/
OBJ = {}; // 報錯——Uncaught TypeError: Assignment to constant variable.
下面的例子和上面同理申窘。
const ARR = [];
ARR.push('Hello'); // 可執(zhí)行
ARR.length = 0; // 可執(zhí)行
ARR = ['Dave']; // 報錯弯蚜,因為ARR重新指向了數組['Dave']所在的內存地址