前言
因为ES6的呈现,变量申明不再枯燥,除了能够用var
外,还能够应用let
和const
。
- ES5:
var
- ES6:
let
、const
上面来理解下它们申明的变量有哪些区别。
1. 变量的挂载不同
- var 申明的变量会挂载在 window 上,而 let 和 const 申明的变量不会
- let 、const 申明的变量会处于以后作用域中
<script>
var a = 100;console.log(window.a); // 100let b = 100;console.log(window.b); // undefinedconst c = 100;console.log(window.c); // undefinedconsole.log(b); // 100 - 以后作用域
波及到作用域无关常识
2.有无变量晋升
- var 申明变量存在变量晋升
- let 和 const 不存在变量晋升
console.log(a);var a = 100; // undefined =》变量晋升,已申明未赋值,默认undefinedconsole.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 =》 未声明应用,报错
能够同时关注下【函数晋升】无关概念
3.是否反复申明
- 同一作用域下 var 能够申明同名变量
- 同一作用域下 let和const不能申明同名变量
var a = 100;console.log(a); // 100var a = 10;console.log(a); // 10let b = 100;let b = 10; // Uncaught SyntaxError: Identifier 'b' has already been declaredif (true) { let b = 10; console.log(b); // 10 => 不同作用域内申明能够}
尽管 var 能够申明同名变量,然而个别不会这么应用。变量名尽可能是惟一的。可关注下【JS变量命名标准】无关。
4.是否有块级作用域
- ES5 是没有块级作用域概念的,所以var申明天然没有
- let 和 const 申明造成块级作用域
if (true) { var a = 100; let b = 10; const c = 10;}console.log(a); // 100console.log(b); // Uncaught ReferenceError: b is not definedconsole.log(c); // Uncaught ReferenceError: c is not defined
可关注 ES5 是如何模仿块级作用域的
5. 暂时性死区
let/const 存在暂时性死区,var 没有。上面新开题目详解。
6.const申明注意事项
- 一旦申明必须赋值,不能用 null 占位
- 申明一个常量,申明后不能再批改
- 如果申明的是复合类型数据,能够批改其属性
const a = 100; // a = 200; // Uncaught TypeError: Assignment to constant variableconst 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)这个区域,不再受内部的影响。
如果在申明变量或常量之前应用它, 会引发 ReferenceError
, 这在语法上成为 暂存性死区
(temporal dead zone,简称 TDZ)。
因为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
不再是一个百分之百平安的操作
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 是在还没申明完应用,于是抛出谬误。
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 还未声明完,处于暂存死区,故报错。
- switch case中case语句的作用域
switch (x) { case 0: let foo; break; case 1: let foo; // TypeError for redeclaration. break;}
会报错是因为switch中只存在一个块级作用域, 改成以下模式能够防止:
let x = 1;switch(x) { case 0: { let foo; break; } case 1: { let foo; break; }}
总结
暂时性死区是一个新概念,咱们应该保持良好变量申明习惯,尽量避免触发。