var 声明与变量提升

var是js的变量声明语句,使用var声明的变量,无论其声明的实际语句在何处,都会被提升到所在函数的顶部(如果声明不在任意函数内,则视为在全局作用域的顶部),这种特性就叫做变量提升。声明式函数也有同样特性

我们来解释一下变量提升:

// 浏览器V8处理前
console.log(single)   //undefined
var single=1

// 浏览器V8引擎处理后,实际执行顺序如下
var single;
console.log(single)   //undefined
single=1

很明显,single无论在哪声明都会被提升到所在函数的顶部(如果声明不在任意函数内,则视为在全局作用域的顶部)

变量提升的处理如下:变量声明=>初始化赋值

块级声明

块级声明也就是让所声明的变量在指定块的作用域外无法被访问。块级作用域(又被称为词法作用域)在如下情况被创建:

  • 在一个函数内部
  • 在一个代码块(由一对花括号包裹)内部

块级作用域是很多类 C 语言的工作机制, ES6 引入块级声明,是为了给 JS 添加灵活性以及与其他语言的一致性。

let 声明

let 声明的语法与 var 的语法一致。你基本上可以用let来代替var进行变量声明,但会将变量的作用域限制在当前代码块中

  • let没有变量提升,因此你需要手动将 let 声明放置到顶部
  • 在同一块级作用域中let禁止重复声明
  • let不会覆盖全局变量,而va声明的变量会,如window,因此少用var去声明全局变量
  • let会有块级作用域绑定,因为let没有变量声明,因此在let未声明定义之前,变量不能访问,这又叫暂时性死区(TDZ)

const常量声明

功能与let基本相似,不同之处如下

  • let声明变量,const声明常量
  • const 声明会阻止对于变量绑定与变量自身值的修改,但不会阻止对变量成员的修改

循环内的 let 声明

规范中规定了 let 声明在循环内部的行为:在每次迭代中,都会创建一个新的 同名变量并对其进行初始化。

大家都众所周知的例子,依次循环打印0-9,现在用let可以这样写:

var funcs = [];
for (let i = 0; i < 10; i++) {
    funcs.push(function() {
        console.log(i);
    });
}
funcs.forEach(function(func) {
    func(); // 从 0 到 9 依次输出
})

而以前用var得这么写:

var funcs = [];
for (var i = 0; i < 10; i++) {
    funcs.push((function(value) {
        return function() {
        console.log(value);
    }
    }(i)));
}
funcs.forEach(function(func) {
    func(); // 从 0 到 9 依次输出
});

所以,以后循环多用let吧!
那么用const行不行呢?如下:

var funcs = [];
// 会报错!!
for (const i = 0; i < 10; i++) {
    funcs.push(function() {
        console.log(i);
    });
}
funcs.forEach(function(func) {
    func(); // 从 0 到 9 依次输出
})

为什么会报错?const 声明会阻止对于变量绑定与变量自身值的修改

总结

let 与 const 块级绑定将词法作用域引入了 JS 。这两种声明方式都不会进行提升,并且 只会在声明它们的代码块内部存在。由于变量能够在必要位置被准确声明,其表现更加接近 其他语言,并且能减少无心错误的产生。作为一个副作用,你不能在变量声明位置之前访问 它们,即便使用的是 typeof 这样的安全运算符。由于块级绑定存在暂时性死区( TDZ ), 试图在声明位置之前访问它就会导致错误。

et 与 const 的表现在很多情况下都相似于 var ,然而在循环中就不是这样。在 for-in 与 for-of 循环中, let 与 const 都能在每一次迭代时创建一个新的绑定,这意味着在循 环体内创建的函数可以使用当前迭代所绑定的循环变量值(而不是像使用 var 那样,统一使 用循环结束时的变量值)。这一点在 for 循环中使用 let 声明时也成立,不过在 for 循 环中使用 const 声明则会导致错误。

块级绑定当前的最佳实践就是:在默认情况下使用 const ,而只在你知道变量值需要被更改 的情况下才使用 let 。这在代码中能确保基本层次的不可变性,有助于防止某些类型的错 误。