es6 的 let 所聲明的變量,只在 let 命令所在的代碼塊內有效。for循環的計數器,就很適合用let命令:javascript
for (let i=0; i< 10; i++) { //.. } console.log(i) // ReferenceError: i is not defined
由於計數器 i 只在 for 循環體內有效,在循環體外引用就會報錯。java
for循環有一個特別之處,設置循環變量的那部分是一個父做用域,而循環體內是一個單獨的子做用域。es6
for(let i =0; i< 3; i++) { let i = 'abc'; console.log(i); } // abc // abc // abc
上面代碼輸出了 3 次 abc, 這代表函數內部的變量 i 和循環變量 i 不在同一個做用域,有各自獨立的做用域;編程
咱們知道, var 聲明的變量會存在變量提高現象,可是 let 不會,let 聲明的變量必定在聲明後使用。不然報錯。好比:數組
// es5 console.log(i); // undefined var i = 10; // es6 console.log(j);// ReferenceError let j = 10;
只要塊級做用域內存在 let 和 const 命令,它所聲明的變量就「綁定」在這個區域,再也不受外部的影響。凡是在聲明以前就使用這些變量,就會報錯,這種稱爲「暫時性死區(TDZ)「;瀏覽器
TDZ的本質是隻要進入當前做用域,所要使用的變量就已經存在了,可是不可獲取,只有等到聲明變量的那一行代碼出現,才能夠獲取和使用該變量。數據結構
let i = 5; if(true) { console.log(i);// Uncaught ReferenceError: i is not defined let i=10; }
雖然全局做用域內有 i 這個變量,可是局部做用域裏用 let 聲明瞭一個同名變量,因此最後 i 就被綁定到局部做用域裏,它未聲明就使用,因此會報錯。ide
這樣的設計是爲了讓你們養成良好的編程習慣,變量必定要在聲明以後使用。不然就報錯。函數
let不容許在相同做用域內,重複聲明同一個變量;es5
function fuc() { let a= 10; let a = 1; }// 報錯 function func () { let a= 10; { let a = 1; } }// 不報錯,不屬於同一做用域
es5 只有全局做用域和函數做用域,沒有塊級做用域,這產生不少不合理的場景。
場景1,內層變量可能會覆蓋外層變量
var tmp = new Date(); function f() { console.log(tmp); if(false) { var tmp = 'hello world' } } f()// undefined;
if 代碼塊的外部使用外層的 tmp 變量,內部使用內層的 tmp 變量,可是。函數 f 執行後,輸出爲undefined;緣由在於變量提高致使的 tmp 變量覆蓋了外層的 tmp 變量;
場景2, 用來計數的循環變量泄露爲全局變量
var s = 'hello'; for(var i = 0; i < s.length; i++) { console.log(i); } conole.log(i);
這裏,i 只是用來控制循環, 可是它沒有消失,泄露成了全局變量。
函數能不能在塊級做用域中聲明?
es5 規定,函數只能在頂層做用域和函數做用域之中聲明,不能在塊級做用域中聲明。
function f() { console.log('outside');} (function () { if(false) { // 重複聲明一次函數 function () { console.log('inside');} } f(); }());
這串代碼在 es5 中運行,照規定來講是非法的,可是爲了兼容,仍是支持在塊級做用域中聲明函數,所以會獲得’inside'; 由於在 if 中聲明的函數會被提高到函數頭部,實際執行的代碼爲:
function f() { console.log('outside');} (function () { function f() {console.log('inside');} if (false) { } f(); }());
在 es6中 執行會怎麼樣?會報錯。
es6規定,函數能夠在塊級做用域內聲明。函數聲明的語句相似於let,在塊級做用域以外不能夠引用。
上面的代碼在符合es6的瀏覽器中,實際運行的是:
function f() { console.log('outside');} (function () { var f = undefined; if (false) { function f() {console.log('inside');} } f(); }());
因此咱們應該避免在塊級做用域內聲明函數,若是須要,函數聲明應該寫成函數表達式,如
{ let a='haha'; let f = function () { return a; } }
塊級做用域不會返回值,咱們能夠將塊級做用域變爲表達式。在 {} 以前加上do, 而後就會返回內部最後執行的表達式的值。不過如今這只是個提案,如今這樣寫 do是瀏覽器沒法解析的。
let x = do { let t = f(); t*t+1; }
const定義的是常量,它不是指變量的值不能改動,而是變量指向的那個內存地址不能改動。對於簡單類型的數據,值就保存在變量指向的那個內存地址,所以等同於常量,而符合類型的數據(數組和對象),變量指向的內存地址,保存的只是一個指針, const只能保證這個指針式固定的,而它指向的數據結構是不可控的。
let 和 const 的特性有: