這裏寫目錄標題
let 和 const的特性
1.不會被提升
console.log(a) // Uncaught ReferenceError: Cannot access 'a' before initialization
console.log(b) // Uncaught ReferenceError: Cannot access 'b' before initialization
let a = 1
const b =2
2.不可重複聲明
var a = 1
const a = 2 // Uncaught SyntaxError: Identifier 'a' has already been declared
var b = 1
let b = 2 // Uncaught SyntaxError: Identifier 'b' has already been declared
3.不綁定全局作用域
let a = 1;
const b = 2
console.log(window.a); // undefined
console.log(window.b); // undefined
拓展延申(開發中,要知道不綁定全局作用域就夠了,有興趣的可以研究下拓展部分)
問:既然不綁定在window上,那它們究竟綁定在哪呢?
要想知道答案,得先了解JS的詞法環境,可以參考這篇文章,下面引用別人的一段話來稍作解釋
ES6 開始,有別於全局環境記錄,有一個單獨的聲明環境記錄,它關聯一個詞法環境對象 (Lexical Environment Object) 來存儲那些不是通過 var function 關鍵字進行的標識符聲明,並且它們都是不可見的。
簡而言之
var
和function
變量的聲明和保存是在變量環境組件中的,即VariableEnvironment
let
和const
變量的聲明和保存以及外部環境引用是在詞法環境組件中的,即LexicalEnvironment
4.只在代碼塊內有效
{
var a = 0;
let b = 1;
}
console.log(a) // 0
console.log(b) // Uncaught ReferenceError: b is not defined
經典for循環
var arr = []
for (var i = 0; i < 5; i++) {
arr[i] = function () {
console.log(i)
}
}
arr[3](); // 打印結果:5
因爲var
聲明的變量,會被提升,上面的代碼等價於下面的代碼,變量i
在循環結束後被賦值爲5
var arr = []
var i
for (i = 0; i < 5; i++) {
arr[i] = function () {
console.log(i)
}
}
arr[3](); // 打印結果:5
想要輸出結果爲3的話,ES6的let
爲此問題提供瞭解決方法
var arr = []
for (let i = 0; i < 5; i++) {
arr[i] = function () {
console.log(i)
}
}
arr[3](); // 打印結果:3
那爲什麼let
它就能解決呢?
一個被提到的比較多的原因是,let
變量不提升,可就算不提升,最後i
還是變成5,輸出也應該是5啊?!!
很多文章中都對這一問題有過解答
如果要追究這個問題,就要拋棄掉之前所講的這些特性!這是因爲 let 聲明在循環內部的行爲是標準中專門定義的,在早期的 let 實現中本就不包含這一行爲,後來專門爲這一問題加入這一行爲
既然是專門定義的,就肯定有其特殊之處,那特殊在哪呢?
在 for 循環中使用 let 和 var,底層會使用不同的處理方式。
那麼當使用 let 的時候底層到底是怎麼做的呢?
簡單的來說,就是在 for (let i = 0; i < 3; i++) 中,即圓括號之內建立一個隱藏的作用域
然後每次迭代循環時都創建一個新變量,並以之前迭代中同名變量的值將其初始化。
這樣對於下面這樣一段代碼
var arr = []
for (let i = 0; i < 5; i++) {
arr[i] = function () {
console.log(i)
}
}
arr[3](); // 打印結果:3
就相當於:
// 僞代碼
(let i = 0) {
arr[0] = function() {
console.log(i)
};
}
(let i = 1) {
arr[1] = function() {
console.log(i)
};
}
(let i = 2) {
arr[2] = function() {
console.log(i)
};
};
當執行函數的時候,根據詞法作用域就可以找到正確的值,其實你也可以理解爲 let 聲明模仿了閉包的做法來簡化循環過程。