在講解es6以前,咱們必需要提一下es5中的var,也就是曾經的那個錯誤。es6
if (condition) {
var value = 1;
}
console.log(value);
複製代碼
很簡單的分析一下,初學者可能會認爲,只有在condition爲true的時候,value纔會被賦值,若是condition爲false的時候,代碼應該會報錯纔對!可是,事實並非這樣的,瀏覽器在執行這段代碼的時候,並非這麼解析的!瀏覽器
var value;
if (condition) {
value = 1;
}
console.log(value);
複製代碼
這樣,很容易咱們就能判斷出,若是condition的值爲false的時候,console出來的值爲undefined。bash
緣由就是咱們常說的,變量提高。閉包
爲了增強對變量生命週期的控制,ECMAScript 6 引入了塊級做用域。函數
塊級做用域存在於:post
函數內部ui
塊中(字符 { 和 } 之間的區域)es5
let和const的特色:spa
1.不會被提高(真的是這樣的嗎?)3d
if (condition) {
let value = 1;
}
console.log(value); // Uncaught ReferenceError: value is not defined
複製代碼
在代碼塊外面訪問,直接斷定,未定義。
2.重複聲明報錯
var value = 1;
let value = 2; // Uncaught SyntaxError: Identifier 'value' has already been declared
複製代碼
這在之前是徹底能夠的,後定義的回覆蓋之前的。
3.不綁定全局做用域
let value = 1 ;
console.log(window.value);
複製代碼
const也是相同的,都訪問不到。
const和let的區別:
const 用於聲明常量,其值一旦被設定不能再被修改,不然會報錯。
值得一提的是:const 聲明不容許修改綁定,但容許修改值。這意味着當用 const 聲明對象時:
const data = {
value: 1
}
// 沒有問題
data.value = 2;
data.num = 3;
// 報錯
data = {}; // Uncaught TypeError: Assignment to constant variable.
複製代碼
臨時死區(Temporal Dead Zone),簡寫爲 TDZ。
let 和 const 聲明的變量不會被提高到做用域頂部,若是在聲明以前訪問這些變量,會致使報錯。
console.log(typeof value); // Uncaught ReferenceError: value is not defined
let value = 1;
複製代碼
來個例子~
var value = "global";
// 例子1
(function() {
console.log(value);
let value = 'local';
}());
// 例子2
{
console.log(value);
const value = 'local';
};
複製代碼
結果是:都報錯了~!
這是由於 JavaScript 引擎在掃描代碼發現變量聲明時,要麼將它們提高到做用域頂部(遇到 var 聲明),要麼將聲明放在 TDZ 中(遇到 let 和 const 聲明)。訪問 TDZ 中的變量會觸發運行時錯誤。只有執行過變量聲明語句後,變量纔會從 TDZ 中移出,而後方可訪問。 循環中的塊級做用域:
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = function () {
console.log(i);
};
}
funcs[0](); // 3
複製代碼
如何改變現狀呢?我要的是funcs0 == 0
在沒有es6 以前,這個事兒麻煩了,還得使用閉包的方式!
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs[i] = (function(i){
return function() {
console.log(i);
}
}(i))
}
funcs[0](); // 0
複製代碼
ES6 的 let 爲這個問題提供了新的解決方法:
var funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function () {
console.log(i);
};
}
funcs[0](); // 0
複製代碼
問題在於,上面講了 let 不提高,不能重複聲明,不能綁定全局做用域等等特性,但是爲何在這裏就能正確打印出 i 值呢? 若是是不重複聲明,在循環第二次的時候,又用 let聲明瞭i,應該報錯呀,就算由於某種緣由,重複聲明不報錯,一遍一遍迭代,i 的值最終仍是應該是 3 呀,還有人說 for 循環的 設置循環變量的那部分是一個單獨的做用域,就好比:
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
複製代碼
這個例子是對的,若是咱們把 let 改爲 var 呢?
for (var i = 0; i < 3; i++) {
var i = 'abc';
console.log(i);
}
// abc
複製代碼
經查, for 循環中使用 let 和 var,底層會使用不一樣的處理方式。 簡單的來講,就是在 for (let i = 0; i < 3; i++) 中,即圓括號以內創建一個隱藏的做用域,這就能夠解釋爲何:
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
複製代碼
而後每次迭代循環時都建立一個新變量,並以以前迭代中同名變量的值將其初始化。
var funcs = [];
for (let i = 0; i < 3; i++) {
funcs[i] = function () {
console.log(i);
};
}
funcs[0](); // 0
複製代碼
至關於:
// 僞代碼
(let i = 0) {
funcs[0] = function() {
console.log(i)
};
}
(let i = 1) {
funcs[1] = function() {
console.log(i)
};
}
(let i = 2) {
funcs[2] = function() {
console.log(i)
};
};
複製代碼
首先明確一點:提高不是一個技術名詞。
要搞清楚提高的本質,須要理解 JS 變量的 這就解釋了爲何在 let x 以前使用 x 會報錯:
假設有以下代碼:
function fn(){
var x = 1
var y = 2
}
fn()
複製代碼
在執行 fn 時,會有如下過程(不徹底):
進入 fn,爲 fn 建立一個環境。
找到 fn 中全部用 var 聲明的變量,在這個環境中「建立」這些變量(即 x 和 y)。
將這些變量「初始化」爲 undefined。
開始執行代碼
x = 1 將 x 變量「賦值」爲 1
y = 2 將 y 變量「賦值」爲 2
也就是說 var 聲明會在代碼執行以前就將「建立變量,並將其初始化爲 undefined」。
這就解釋了爲何在 var x = 1 以前 console.log(x) 會獲得 undefined。
接下來來看 function 聲明的「建立、初始化和賦值」過程
假設代碼以下:
fn2()
function fn2(){
console.log(2)
}
複製代碼
JS 引擎會有一下過程:
找到全部用 function 聲明的變量,在環境中「建立」這些變量。
將這些變量「初始化」並「賦值」爲 function(){ console.log(2) }。
開始執行代碼 fn2()
也就是說 function 聲明會在代碼執行以前就「建立、初始化並賦值」。
接下來看 let 聲明的「建立、初始化和賦值」過程
{
let x = 1
x = 2
}
複製代碼
咱們只看 {} 裏面的過程:
找到全部用 let 聲明的變量,在環境中「建立」這些變量
開始執行代碼(注意如今尚未初始化)
執行 x = 1,將 x 「初始化」爲 1(這並非一次賦值,若是代碼是 let x,就將 x 初始化爲 undefined)
執行 x = 2,對 x 進行「賦值」
let x = 'global'
{
console.log(x) // Uncaught ReferenceError: x is not defined
let x = 1
}
複製代碼
這就解釋了爲何在 let x 以前使用 x 會報錯: 緣由有兩個:
console.log(x) 中的 x 指的是下面的 x,而不是全局的 x
執行 log 時 x 還沒「初始化」,因此不能使用(也就是所謂的暫時死區)
看到這裏,你應該明白了 let 到底有沒有提高:
let 的「建立」過程被提高了,可是初始化沒有提高。
var 的「建立」和「初始化」都被提高了。
function 的「建立」「初始化」和「賦值」都被提高了。
最後看 const,其實 const 和 let 只有一個區別,那就是 const 只有「建立」和「初始化」,沒有「賦值」過程!!!
這四種聲明,用下圖就能夠快速理解:
所謂暫時死區,就是不能在初始化以前,使用變量。