ES5只有全局作用域和函数作用域,没有块级作用域,而变量的声明赋值实际上是分开的,变量的声明会提升到当前作用域的顶部,赋值则是按正常的代码执行顺序赋值,因此这样就会引发一些代码执行问题(面试要考的,划重点),例如:

var abc = "123";

function test() {
    console.log(abc); // undefined
    if (false) {
        var abc = '不会执行的赋值语句';
    }
}

test();

// 实际解释后的代码
//
// var abc = "123";
// function test() {
//     var abc; // 提升
//     console.log(abc); // undefined
//     if (false) {
//         abc = '不会执行的赋值语句';
//     }
// }
// test();

由于变量提升 (hoisting),上面的代码中,函数中 if 语句内的变量声明会提升至函数作用域,因此会导致console.log(abc)运行时输出的变量值为undefined,从而覆盖了全局作用域的同名变量

同理,在一些循环语句中声明的变量也会有类似的问题,例如:

var abc = '123';

for (var i = 0, l = abc.length; i < l; i++) {
    // todo
}

console.log(i); // 3

// 实际解释后的代码
//
// var abc;
// var i;
// abc = '123';
// for (i = 0, l = abc.length; i < l; i++) {
//     // todo
// }
// console.log(i); // 3


因此,ES6 引入了可任意嵌套的块级作用域,使用{}包裹,同时引入了letconst两种新的变量类型,它们都允许开发人员使用块级作用域,这是一个很有用的改进,它消除了变量提升 (hoisting)引起的歧义,使得JavaScript变得更容易理解了。每层的块级作用域只能获取到自己以及自己的上层作用域定义的变量,例如:

{
{
{
let a = 123;
{
console.log(a); // 123
}
}
console.log(a); // ReferenceError: a is not defined
}
}

请注意:对于 var 声明的变量仍然会提升到全局作用域或是函数作用域,例如上面的代码,let 若改成 var,则都会正常输出 123