1. 有 3 个关键字可以声明变量:var、const 和 let。

2-1 var关键字

1. var 声明作用域

  1. 关键的问题在于,使用 var 操作符定义的变量会成为包含它的函数的局部变量。比如,使用 var在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁:
  2. function test() {
  3. var message = "hi"; // 局部变量
  4. }
  5. test();
  6. console.log(message); // 出错
  1. 在函数内定义变量时省略 var 操作符,可以创建一个全局变量:
  2. function test() {
  3. message = "hi"; // 全局变量
  4. }
  5. test();
  6. console.log(message); // "hi"

2 var 声明提升

  1. function foo() {
  2. console.log(age);
  3. var age = 26;
  4. }
  5. foo(); // undefined
  6. 之所以不会报错,是因为 ECMAScript 运行时把它看成等价于如下代码:
  7. function foo() {
  8. var age;
  9. console.log(age);
  10. age = 26;
  11. }
  12. foo(); // undefined

2-2 let声明

  1. let 跟 var 的作用差不多,但有着非常重要的区别。最明显的区别是,let 声明的范围是块作用域,
  2. 而 var 声明的范围是函数作用域
  1. if (true) {
  2. let age = 26;
  3. console.log(age); // 26
  4. }
  5. console.log(age); // ReferenceError: age 没有定义
  1. let 也不允许同一个块作用域中出现冗余声明。这样会导致报错:
  2. var name;
  3. var name;
  4. let age;
  5. let age; // SyntaxError;标识符 age 已经声明过了
  1. 当然,JavaScript 引擎会记录用于变量声明的标识符及其所在的块作用域,因此嵌套使用相同的标识符不会报错,而这是因为同一个块中没有重复声明:
  2. var name = 'Nicholas';
  3. console.log(name); // 'Nicholas'
  4. if (true) {
  5. var name = 'Matt';
  6. console.log(name); // 'Matt'
  7. }
  8. let age = 30;
  9. console.log(age); // 30
  10. if (true) {
  11. let age = 26;
  12. console.log(age); // 26
  13. }

全局变量

  1. 与 var 关键字不同,使用 let 在全局作用域中声明的变量不会成为 window 对象的属性(var 声
  2. 明的变量则会)。
  3. var name = 'Matt';
  4. console.log(window.name); // 'Matt'
  5. let age = 26;
  6. console.log(window.age); // undefined
  1. 对于 let 这个新的 ES6 声明关键字,不能依赖条件声明模式。

for 循环中的 let 声明

  1. 在使用 var 的时候,最常见的问题就是对迭代变量的奇特声明和修改:
  2. for (var i = 0; i < 5; ++i) {
  3. setTimeout(() => console.log(i), 0)
  4. }
  5. // 你可能以为会输出 0、1、2、3、4
  6. // 实际上会输出 5、5、5、5、5
  7. 在使用 let 声明迭代变量时,JavaScript 引擎在后台会为每个迭代循环声明一个新的迭代变量。
  8. for (let i = 0; i < 5; ++i) {
  9. setTimeout(() => console.log(i), 0)
  10. }
  11. // 会输出 0、1、2、3、4
  12. 这种每次迭代声明一个独立变量实例的行为适用于所有风格的 for 循环,包括 for-in 和 for-of
  13. 循环

2-3 const 声明

  1. const 的行为与 let 基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量,且尝试修改 const 声明的变量会导致运行时错误。
  1. const age = 26;
  2. age = 36; // TypeError: 给常量赋值
  3. // const 也不允许重复声明
  4. const name = 'Matt';
  5. const name = 'Nicholas'; // SyntaxError
  6. // const 声明的作用域也是块
  7. const name = 'Matt';
  8. if (true) {
  9. const name = 'Nicholas';
  10. }
  11. console.log(name); // Matt
  1. const 声明的限制只适用于它指向的变量的引用。换句话说,如果 const 变量引用的是一个对象,
  2. 那么修改这个对象内部的属性并不违反 const 的限制。
  3. const person = {};
  4. person.name = 'Matt'; // ok
  1. 如果你只想用 const 声明一个不会被修改的 for 循环变量,那也是可以的。也就是说,每
  2. 次迭代只是创建一个新变量。这对 for-of 和 for-in 循环特别有意义:
  3. let i = 0;
  4. for (const j = 7; i < 5; ++i) {
  5. console.log(j);
  6. }
  7. // 7, 7, 7, 7, 7
  8. for (const key in {a: 1, b: 2}) {
  9. console.log(key);
  10. }
  11. // a, b
  12. for (const value of [1,2,3,4,5]) {
  13. console.log(value);
  14. }
  15. // 1, 2, 3, 4, 5

2-4 声明风格及最佳实践

  1. 1. 不使用 var
  2. 有了 let 和 const,大多数开发者会发现自己不再需要 var 了。限制自己只使用 let 和 const
  3. 有助于提升代码质量,因为变量有了明确的作用域、声明位置,以及不变的值。
  1. 2. const 优先,let 次之
  2. 使用 const 声明可以让浏览器运行时强制保持变量不变,也可以让静态代码分析工具提前发现不合法的赋值操作。因此,很多开发者认为应该优先使用 const 来声明变量,只在提前知道未来会有修改时,再使用 let。这样可以让开发者更有信心地推断某些变量的值永远不会变,同时也能迅速发现因意外赋值导致的非预期行为。