同一個做用域內不能重複聲明同一個變量:數組
function func() { let a = 10; var a = 1; } func() // 報錯 function func() { let a = 10; let a = 1; } func() // 報錯
function func(arg) { let arg; } func() // 不能在函數內部從新聲明參數!!!!!由於參數等同於在函數內部var聲明的一個局部變量 function func(arg) { { let arg; } } func() // 該例子不報錯,是由於在func裏面增長{}等同於又新建了一個做用域,所以{}裏的arg不是參數arg
let能夠只聲明不賦值,沒有值的時候會輸出undefined閉包
console.log(let1) // let1 is not defined let let1; console.log(let1) // undefined let1 = 1; console.log(let1) // 1
var a = []; for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 10
該demo的執行過程以下:函數
var a = []; // 全局變量spa
var i = 0; // 全局變量,第1次循環指針
a[0] = function(){ console.log(i) } // 此時的i不是0,是由於這裏只是聲明瞭這個函數,並無執行它,即沒有建立這個函數的上下文,所以i不會沿着做用域鏈向外去找i的值code
var i = 1; // 第2次循環,由於是全局變量,所以i = 1會替換上一個i = 0對象
a[1] = function(){ console.log(i) } // 同a[0],全部該函數的執行的做用域是全局做用域blog
var i = 2 // 第3次循環,同理,i = 2會替換上一個i = 1內存
...作用域
var i = 9; // 第10次循環,i = 9會替換上一個i = 8
a[9] = function(){ console.log(i) }
直到var i = 10,10<10不知足循環條件,則跳出循環,繼續向下執行全局做用域下面的語句:a[6]();
調用a[6]函數,並建立a[6]函數的函數上下文,執行該函數內部的console.log(i),這個函數中沒有i,所以順着做用域鏈向外去找i,而此時全局變量var i = 10,所以輸出10
var a = []; for (let i = 0; i < 10; i++) { a[i] = function () { console.log(i); }; } a[6](); // 6
第二個demo的執行過程以下:
let a = []; // 全局變量
{ // 第1次循環
let i = 0; // let使得for循環變成一個塊級做用域,則let i = 0是這個塊級做用域下的局部變量
a[0] = function(){ console.log(i) } // 注意!!!因爲let的聲明建立了一個塊級做用域,此時的a[0]這個函數就是一個閉包
}
{ // 第2次循環
let i = 1; // 注意!!!此時的let i = 1和let i = 0是屬於兩個不一樣的塊級做用域,所以二者的值並不會相互影響
a[1] = function(){ console.log(i) } // 同a[0]
}
...
{ // 第10次循環
let i = 9;
a[9] = function(){ console.log(i) } // 一樣該函數也是一個閉包!
}
{
直到let i = 10,不知足循環條件,跳出循環,注意!!該代碼塊中不存在閉包所以,let i = 10在此次循環以後代碼塊隨即被銷燬
}
繼續向下執行全局做用域下面的語句:a[6]();
調用a[6]函數,進入該塊級做用域的代碼環境,在該閉包內部找i值,若是沒有則順着做用域鏈向外去找i,而此時塊級做用域內有let i = 6,所以輸出6
此時閉包被調用,因此整個代碼塊中的變量i和a[6]函數被銷燬。
在代碼塊內,凡是用let聲明變量以前,該變量都是不可以使用的,這在語法上叫作「暫時性死區」(Temporal Dead Zone,簡稱TDZ)。因此,凡是在聲明以前使用該變量,就會報錯!
var tmp = 'ning'; if (true) { // TDZ開始 tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError 去掉這兩句才能繼續向下執行代碼並輸出響應的結果 let tmp; // TDZ結束 console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123 }
從上面的代碼能夠看出雖然全局有一個tmp變量,可是在if這個塊級做用域下let聲明瞭一個局部變量tmp,致使該局部變量tmp綁定(binding)了這個區域,因此在let聲明以前使用它,都屬於該tmp的死區,會報錯!
在沒有let以前,使用typeof是百分之百不會報錯的,由於若是typeof一個未聲明的變量,會輸出'undefined'
可是,暫時性死區的出現使得typeof的使用須要當心!由於若是在let聲明前typeof該變量則會報錯!
typeof x; // ReferenceError let x;
const聲明一個只讀的常量。一旦聲明其值就不能改變,而且一旦聲明變量就必須當即初始化,只聲明不賦值就會報錯!
console.log(const1) // Missing initializer in const declaration const const1; console.log(const1) // Missing initializer in const declaration
cosnt也是在塊級做用域內有效,例子以下:
if (true) { const MAX = 5; } console.log(MAX); // Uncaught ReferenceError: MAX is not defined
const命令聲明的常量不能提高,只能在聲明的位置以後使用const,例子以下:
if (true) { console.log(MAX); // Uncaught ReferenceError: MAX is not defined const MAX = 5; }
const一樣不能重複聲明!例子以下:
function func() { const a = 10; var a = 1; } func() // 報錯 function func() { const a = 10; let a = 1; } func() // 報錯
其實是保證的是變量指向的內存地址保存的數據不能改動!
對於基本數據類型而言,內存地址裏保存的就是值,因此等同於常量
而對於引用類型而言(主要是對象和數組),變量指向的內存地址中保存的是 指向實際數據的指針,所以只要該指針是固定不變的,該指針指向的堆內存的數據是否變化const是不在乎的!所以,將一個對象聲明爲常量必須很是當心!!!!例子以下:
const obj = {}; obj.attr = 'ning'; console.log(obj.attr) // 'ning' obj = { attr: 'li' } console.log(obj.attr) // Uncaught TypeError: Assignment to constant variable.
常量obj存的是一個地址,指向一個對象,不可變的是這個地址,而對象自己裏的內容是可變的!