因爲ES6的出現,變量聲明再也不單調,除了能夠用var
外,還可使用let
和const
。安全
var
let
、const
下面來了解下它們聲明的變量有哪些區別。app
<script>
var a = 100; console.log(window.a); // 100 let b = 100; console.log(window.b); // undefined const c = 100; console.log(window.c); // undefined console.log(b); // 100 - 當前做用域
涉及到做用域有關知識
console.log(a); var a = 100; // undefined =》變量提高,已聲明未賦值,默認undefined console.log(b); let b = 100; // Uncaught ReferenceError: Cannot access 'b' before initialization =》 未聲明使用,報錯 console.log(c); let c = 100; // Uncaught ReferenceError: Cannot access 'b' before initialization =》 未聲明使用,報錯
能夠同時關注下【函數提高】有關概念
var a = 100; console.log(a); // 100 var a = 10; console.log(a); // 10 let b = 100; let b = 10; // Uncaught SyntaxError: Identifier 'b' has already been declared if (true) { let b = 10; console.log(b); // 10 => 不一樣做用域內聲明能夠 }
雖然 var 能夠聲明同名變量,可是通常不會這麼使用。變量名儘量是惟一的。可關注下【JS變量命名規範】有關。
if (true) { var a = 100; let b = 10; const c = 10; } console.log(a); // 100 console.log(b); // Uncaught ReferenceError: b is not defined console.log(c); // Uncaught ReferenceError: c is not defined
可關注 ES5 是如何模擬塊級做用域的
let/const 存在暫時性死區,var 沒有。下面新開標題詳解。函數
const a = 100; // a = 200; // Uncaught TypeError: Assignment to constant variable const list = []; list[0] = 10; console.log(list); // [10] const obj = {a:100}; obj.name = 'apple'; obj.a = 10000; console.log(obj); // {a:10000,name:'apple'}
只要塊級做用域內存在let命令,它所聲明的變量就「綁定」(binding)這個區域,再也不受外部的影響。code
若是在聲明變量或常量以前使用它, 會引起 ReferenceError
, 這在語法上成爲 暫存性死區
(temporal dead zone,簡稱 TDZ)。ip
因爲let、const沒有變量提高,才產生了
暫時性死區
if (true) { // TDZ開始 tmp = 'abc'; // ReferenceError console.log(tmp); // ReferenceError let tmp; // TDZ結束 console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123 }
上面代碼中,在let命令聲明變量tmp以前,都屬於變量tmp的「死區」。內存
在暫時性死區內,typeof
再也不是一個百分之百安全的操做作用域
typeof x; // Uncaught ReferenceError: Cannot access 'y' before initialization =》報錯:未聲明不可用 let x; typeof undefined_variable // undefined =》未聲明的變量不會報錯
function test() { var foo = 100; if (true) { let foo = (foo + 100); // Uncaught ReferenceError: Cannot access 'foo' before initialization } } test();
在 if 語句中,foo 使用 let 進行了聲明,此時在 (foo + 100) 中使用的 foo 是 if 語句中的 foo,而不是外面的 var foo = 100;
因爲賦值運算符是將右邊的值賦予左邊,因此先執行了 (foo + 100), 因此 foo 是在還沒聲明完使用,因而拋出錯誤。it
function team(n) { console.log(n); for (let n of n.member) { // Uncaught ReferenceError: Cannot access 'n' before initialization console.log(n) } } team({member: ['tony', 'lucy']})
在 for 語句中,n 已經進入了塊級做用域,n.member 指向的是 let n ,跟上一例子同樣,此時 n 還未聲明完,處於暫存死區,故報錯。io
switch (x) { case 0: let foo; break; case 1: let foo; // TypeError for redeclaration. break; }
會報錯是由於switch中只存在一個塊級做用域, 改爲如下形式能夠避免:console
let x = 1; switch(x) { case 0: { let foo; break; } case 1: { let foo; break; } }
暫時性死區是一個新概念,咱們應該保持良好變量聲明習慣,儘可能避免觸發。