const 和 let 是ES6 新增的块级作用域变量申明方法,其中const 声明的是常量,他们同var 有哪些区别呢。
var a = 0;
if (true) {
console.log(a)
let a = 0;
console.log(a)
}
这段代码会抛出一个引用错误(ReferenceError),在块级作用域中用 let 申明了变量 a, 但在声明变量前使用 a 会进入TDZ暂时性死区,因为在块级作用域已经找到了变量a,就不会再沿作用域链向上查找别的变量 a,在死区里引用就会 throw
一个 ReferenceError
。
作用域链(Scope Chain)
当代码中引用一个变量时,JavaScript 引擎会从当前作用域开始向上查找该变量,直到找到该变量或者到达全局作用域为止。这个查找过程就是作用域链。如果找到了该变量,就会使用它所在的作用域中的值;否则,就会抛出 ReferenceError 异常。

变量提升🏗 和 let const 的暂时性死区
JavaScript 引擎会在代码执行之前的编译过程中扫描整个作用域,并将所有的函数声明和变量声明“提升”🏗 到作用域的顶部。
对于var , let , const 的创建和初始化都是提升的,并将值初始化为 undefined,但赋值不会提升。
但使用 let 或 const 声明的变量或常量存在“暂时性死区”(Temporal Dead Zone, TDZ),TDZ 是指从当前块级作用域开始到变量声明位置结束的区域,如果在这个区域访问该变量或常量会抛出一个引用错误(ReferenceError)。
对于一个function 它的创建、初始化和赋值是同时提升的。

块级作用域(Block Scope)
ES5 只有全局作用域(Global Scope)和函数作用域(Function Scope),而 ES6 开始有块级作用域(Block Scope),用let const 申明的变量只能在块级作用域里有效。
块级作用域可以避免变量的污染和命名冲突。在块级作用域中声明的变量在块执行完后会被释放,不会污染全局作用域。
var a = 10
let a = 11 // caught SyntaxError: Identifier 'a' has already been declared
{
let a = 12
console.log(a) // 12
}
{
const a = 13
console.log(a) // 13
}

块级作用域以括号{}分开,还有while(){}, if(){} ,try{} catch(e){},for 循环。
for 循环中每一次循环都是一个独立的作用域。
var a = []
for (let i = 0; i < 10; i++) {
a[i] = function () { console.log(i); };
}
a[6](); // output: 6
// 如果将let 改成 var
var a = []
for (var i = 0; i < 10; i++) {
a[i] = function () { console.log(i); };
}
a[6](); // output: 10
// 可以值变参分割作用域
var a = []
for (var i = 0; i < 10; i++) {
(function(i) {
a[i] = function () { console.log(i); }
})(i)
}
a[6](); // output: 6
使用 let 和 const 声明的变量常量将被限制在当前块级作用域内,并且在这个块级作用域外部是不可访问的。
虽然块级作用域可以限制变量的作用域范围,但是它并不会为块中声明的变量分配一个新的执行上下文。在 JavaScript 中,只有函数会创建新的函数执行上下文(Function Execution Context),而块级作用域不会。
