变量的初始化, 重复声明

var定义的变量如果没有初始化, 那么它的初始值默认是undefined

console.log(a);
var a;

let b;
console.log(b);

const定义的变量是一个常量, 必须初始化, 而且初始化之后不能修改绑定, 但允许修改值, 这就意味着用const声明一个对象后, 可以修改该对象的属性值

使用var关键字多次声明同一个变量是合法的, 但是使用let/const多次声明同一个变量是不合法的, let/const禁止重复声明

var a = 10;
var a = 20; // 覆盖前面的变量, 不会报错

let b = 10;
let b = 20; // 会抛出语法错误

var c = 10;
let c = 10; // 报错

作用域和作用域链

作用域就是用于确定在何处以及如何查找变量的一套规则, 控制着变量和函数的可见性和生命周期.
作用域查找会在找到第一个匹配的标识符时停止。在多层的嵌套作用域中可以定义同名的标识符,这叫作“遮蔽效应”(内部的标识符“遮蔽”了外部的标识符)。抛开遮蔽效应,作用域查找始终从运行时所处的最内部作用域开始,逐级向外或者说向上进行,直到遇见第一个匹配的标识符为止。

var关键字声明的全局变量会自动成为全局对象的属性, 这意味着在全局作用域中使用var有可能会覆盖掉一个已经存在的全局属性.

var声明的变量的作用域有全局作用域和函数作用域, 没有块级作用域. let/const声明的变量增加了块级作用域

从 ES3 开始,try/catch 结构在 catch 分句中具有块作用域. 因此在es6之前的环境里可以利用catch实现块级作用域

try {
    throw undefined;
} catch(a) {
    a = 2;
    console.log(a);
}

console.log(a);

提升机制

提升机制: 先声明再赋值. 函数声明和var声明都会被提升. 函数会首先被提升

a = 10;
var a; // 不会报错

a = 20;
let a; // 因为暂时性死区而报错

var funcs = [];
for(var i = 0; i < 10; i++) {
    funcs.push(function() {
        console.log(i);
    });
}
funcs.forEach((item) => {
    item(); // 打印输出10个数字10
});

什么是暂时性死区

只要块级作用域内存在let/const关键字声明的变量, JavaScript会将该变量声明移入暂时性死区中, 访问暂时性死区中的变量会触发运行时错误, 只有在执行过let/const变量声明语句之后, 该变量声明才会从暂时性死区中移除, 之后可以正常访问.

var tmp = 10;
if(true) {
    tmp = 20; // 试图访问暂时性死区中的变量, 报错
    let tmp;
}

function fn(x=y, y=2) {
    return [x, y];
}
fn(); // 报错