因为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; } }
暂时性死区是一个新概念,咱们应该保持良好变量声明习惯,尽可能避免触发。