回顧:JavaScript 的 var 聲明變量
1. 允許重復的變量聲明:導致數(shù)據(jù)被覆蓋酬滤。
var a = 1;
function print() {
console.log(a);
}
// 假設這里有一千行代碼家淤,重復的變量可能會導致變量被更改
var a = 2;
print(); // a = 2
2. 變量提升:怪異的數(shù)據(jù)訪問、閉包問題。
怪異的數(shù)據(jù)訪問:
if(Math.random() < 0.5) {
var a = "abc";
console.log(a);
}else{
console.log(a)
}
console.log(a);
// 邏輯為:如果隨機數(shù)小于0.5威始,則聲明變量a,賦值為abc肤寝,輸出a当辐;如果隨機數(shù)不小于0.5,則輸出a鲤看。
// 變量提升:先聲明變量a缘揪,如果隨機數(shù)小于0.5,賦值為abc义桂,輸出a找筝;否則輸出a。
閉包問題:
<div id="divBtns"></div>
var div = document.getElementById("divBtns");
for(var i = 1; i <= 10; i ++) {
var btn = document.createElement("button");
btn.innerHTML = "按鈕" + i;
div.appendChild(btn);
// 當btn生成后慷吊,循環(huán)結束袖裕,結束之后才開始點擊事件
btn.onclick = function() {
console.log(i); // 使用同一個變量 i
}
}
// 循環(huán)結束后 i = 1
3. 全局變量掛載到全局對象:全局對象成員污染問題。
var abc = "123";
console.log(window.abc); // 會導致window對象的成員被覆蓋
let 聲明變量
ES6不僅引入let關鍵字用于解決變量聲明的問題溉瓶,同時引入了塊級作用域的概念急鳄。
let 聲明的變量只在 let 命令所在的代碼塊內(nèi)有效。
塊級作用域:代碼執(zhí)行時遇到花括號堰酿,會創(chuàng)建一個塊級作用域疾宏,花括號結束,銷毀塊級作用域触创。
let a = 123; // 全局作用域
{
// 塊級作用域
let a = 456;
console.log(a); // 456
}
console.log(a); // 123
1. let聲明的變量不會掛載到全局對象坎藐。
let a = 123;
console.log(window.a); // undefined
2. let聲明的變量,不允許當前作用域范圍內(nèi)重復聲明哼绑。在塊級作用域中用let定義的變量岩馍,在作用域外不能訪問。
let a = 123;
let a = 456; // 檢查到當前作用域(全局作用域)已經(jīng)聲明了a抖韩,會報錯
let a = 123; // 全局作用域
{
// 塊級作用域
let a = 456;
console.log(a); // 456
}
console.log(a); // 123
3. 使用let不會有變量提升蛀恩,因此不能在定義let變量之前,使用let變量帽蝶。
if(Math.random() < 0.5) {
let a = "abc"; // 定義在當前塊級作用域
console.log(a); // 當前塊級作用域的a赦肋。
}else{
// 這是另一個塊級作用域,該作用域找不到a
console.log(a)
}
console.log(a);
底層實現(xiàn)上励稳,let聲明的變量實際上也會有提升佃乘,但是,提升后會將其放入到“暫時性死區(qū)”驹尼,如果訪問的變量位于暫時性死區(qū)趣避,則會報錯“Cannot access 'a' before initialization”。當代碼運行到該變量的聲明語句時新翎,會將其從暫時性死區(qū)中移除程帕。
console.log(a); // Cannot access 'a' before initialization
let a = 123;
4. 在循環(huán)中住练,用let聲明的循環(huán)變量,會特殊處理愁拭,每次進入循環(huán)體讲逛,都會開啟一個新的作用域,并且將循環(huán)變量綁定到該作用域(每次循環(huán)岭埠,使用的是一個全新的變量)盏混。在循環(huán)中,使用let聲明的循環(huán)變量惜论,在循環(huán)結束后會被銷毀许赃。
<div id="divBtns"></div>
<script>
let div = document.getElementById("divBtns");
for(let i = 1; i <= 10; i ++) {
let button = document.createElement("button");
button.innerHTML = "按鈕" + i;
div.appendChild(button);
button.onclick = function() {
console.log(i); // 使用的是當前作用域的 i
}
}
</script>
const聲明常量
const 和 let 完全相同,僅在于用const聲明的變量馆类,必須在聲明時賦值混聊,而且不可以重新賦值。
const a; // 未賦值會報錯:“SyntaxError: Missing initializer in const declaration”
const a = 1;
a = 2; // 重新賦值會報錯:“TypeError: Assignment to constant variable.”
實際上乾巧,在開發(fā)中句喜,應該盡量使用const來聲明變量,以保證變量的值不會被隨意篡改卧抗。原因如下:
- 根據(jù)經(jīng)驗藤滥,開發(fā)中的很多變量,都是不會更改社裆,也不應該更改的。
- 后來的很多框架或是第三方js庫向图,都是要求數(shù)據(jù)不可變泳秀,使用常量可以在一定程度上保證這一點。
注意細節(jié):
1. 常量不可變榄攀,是指聲明的常量的內(nèi)存空間不可變嗜傅,并不保證內(nèi)存空間中的地址指向的其他空間不可變。
const obj = {a: 2};
obj.a = 3;
obj.x = 4;
console.log(obj); // {a: 3, x: 4}
2. 常量的命名
特殊的常量:該常量從字面意義上檩赢,一定是不可變的吕嘀,比如圓周率、月距地或其他一些絕不可能變化的配置贞瞒。通常偶房,該常量的名稱全部使用大寫,多個單詞之間使用下劃線分割军浆。
普通的常量:使用和之間一樣的命名即可棕洋。
3. 在for循環(huán)中,循環(huán)變量不可以使用常量乒融。