1.let命令?
基本概念
let語法類似于var,不同點在于let定義的變量只在定義它的代碼塊中有效。
{
var a = 1;
let b = 2;
}
a // 輸出1
b // 報錯?Uncaught ReferenceError: b is not defined
var 定義的變量要么為全局變量,要么是在函數之中的局部變量。上述代碼塊中的?a?即為全局變量,所以在代碼塊外也可調用此變量。而在代碼塊外調用變量 b 報錯可證明?b?只在定義它的代碼塊中有效。
不存在變量提升和暫時性死區
ES6 中的?let?命令是不存在“變量提升”現象的,變量提升指的是在變量未經定義之前便可調用。
console.log(a);
var a = 1;
// undefined
console.log(b);
let b = 2;
// Uncaught ReferenceError: b is not defined
上述代碼,使用 var 定義的變量 a 發生變量提升,在腳本程序運行時變量已經存在了,只是還未定義值,所以輸出 undefined。而變量 b 在未定義之前調用打印?b?的代碼會報錯,表明使用let?命令定義的變量是不存在變量提升的。
b = 3;
let b = 2;
//?Uncaught ReferenceError: b is not defined
當你輸入上述代碼卻得到報錯的結果是不是很疑惑呀,為什第一句代碼沒有把變量 b 定義為一個全局變量呢?
沒錯!“罪魁禍首” 就是 let 命令,因為從當前作用域的頭部一直到?let 命令聲明變量?b?之前,b都是不可用的,這在語法上稱為暫時性死區(temporal dead zone)。
ES6中規定暫時性死區和let、const不出現變量提升,能夠有效地避免在聲明變量之前就使用它。
重復定義檢查
相同作用域內,var 可以讓同一個變量名在同一個作用域里被定義多次,而?let?則不允許。以下是幾個例子。
{
let a = 1;
var a = 2;
}
{
let b = 2;
let b =3;
}
function test(argument){
let argument = 4;
}
test();
上述三塊代碼均會報出變量名已經聲明的錯誤。
let 用途
下面考慮一種需求:需要動態往HTML中一個ID為“list”的標簽中插入十個li標簽,并且每個li標簽都帶有一個提示本標簽被點擊的點擊事件。
var list = document.getElementById('list');
for( let i=1;i<=10 ;i++){
let item = document.createElement('li');
item.appendChild(document.createTextNode('Item '+i));
item.onclick = function(e){
console.log('Item '+i+' is clicked.');
};
list.appendChild(item);
}
上述代碼利用 for 循環完成了上述需求。這時候你會想這個和?let?命令有什么關系,我換成?var 豈不是也能實現。下面我們來檢驗一下換成?var?可行嗎?
var list = document.getElementById('list');
for( var i=1;i<=10 ;i++){
let item = document.createElement('li');
item.appendChild(document.createTextNode('Item '+i));
item.onclick = function(e){
console.log('Item '+i+' is clicked.');
};
list.appendChild(item);
}
當我們點擊上述代碼生成的?li?標簽時,會發現無論點擊哪個都會打印出“Item 11 is clicked.”。下面我來解釋一下為什么會出現這種情況,因為在?for?循環中的變量?i?是var定義的,在全局范圍內都有效,而每個標簽被點擊所執行的函數內部的?i?都指的是這個全局的?i?。而使用?let?命令時,循環體的每一次執行都產生一個作用域,每次綁定點擊事件時,函數都能保留當前計數器的數值和引用。
注意:
let、const 命令定義的全局變量不屬于頂層對象的屬性。
let a = 1;
window.a // undefined
2.const命令
基本概念
const 命令用來定義常量,一旦聲明,不可改變。這也意味著聲明變量的同時就需要進行初始化,不可留到以后賦值。
const Max_Age;
Max_Age = 100;
// Uncaught SyntaxError: Missing initializer in const declaration
const 命令與?let?命令一樣:
1.只在聲明的塊級作用域有效;
2.常量不可提升,同樣存在暫時性死區;
3.不可重復聲明。
原理
變量與內存之間的關系由三部分組成:變量名、內存綁定及內存地址。const 的實現原理便是在常量名和內存地址之間創建一個不可變的綁定。在某些情況下,并非是值不可變的。對于基本類型(數值,字符串等)而言,常量指向的內存地址便保存著實際值。而對于對象、數組等引用類型,常量指向的只是一個指針,const 只能保證這個指針是不可變的,如下:
const obj = {};
obj.item = 456;
obj.item; // 輸出456
obj = {}; // Uncaught TypeError: Assignment to constant variable.
凍結對象
上述說明當用?const?定義對象時,并不能保證值不可變,下面我們就介紹如何獲取值不可變的對象。除了凍結對象,如果對象的屬性指向的還是對象,那么這個屬性也該被凍結。如下代碼便可獲取值不可變的對象。
const deepFreeze = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach((key,i) => {
if(typeof obj[key] === 'object')
{ deepFreeze(obj[key]);}
});
};
3.建議
1. 一般情況下,使用?const?命令對值進行存儲;
2. 當一個值容器存儲的值確認會被改變時才使用?let?進行定義;
3. 不再使用?var。