• var 声明的范围是函数作用域,let 和 const 声明的范围是块作用域

  • var 声明的变量会被提升到函数作用域的顶部,let 和 const 声明的变量不存在提升,且具有暂时性死区特征

  • var 允许在同一个作用域中重复声明同一个变量,let 和 const 不允许

  • 在全局作用域中使用 var 声明的变量会成为 window 对象的属性,let 和 const 声明的变量则不会

  • const 的行为与 let 基本相同,唯一 一个重要的区别是,使用 const 声明的变量必须进行初始化,且不能被修改

关键字变量提升块级作用域重复声明同名变量重新赋值
var×
let××
const×××

1.作用域

var没有块级作用域,而let声明的范围是块作用域; 一对大括号 就是 一个块级作用域

if (true) {
    var message = "hello";
    console.log(message); // hello
}
console.log(message); // hello


if (true) {
    let message = "hello";
    console.log(message); // hello
}
console.log(message); // error: message is not defined

let 不允许同一个块作用域中出现冗余声明

if (true) { 
    // error: 无法重新声明块范围变量“a”
    let a;
    let a;
}

JS 引擎会记录用于变量声明的标识符及其所在的块作用域,因此嵌套使用相同的标识符不会报错,这是因为同一个块中没有重复声明:

let a = 666;
console.log(a); // 666
if (true) {
    let a = '啊哈哈';
    console.log(a); // 啊哈哈
}

varlet声明的并不是不同类型的变量,它们只是指出变量在相关作用域如何存在,所以对声明冗余报错不会因混用varlet而受影响:

// error
var a;
let a;

// error
let b;
var b;

2.变量提升

//用var命名的变量有变量提升
console.log(num1);  // undefined
var num1 = 10;
// 以上代码运行时,相当于下面的写法
​
var num2;  // 声明提升到作用域最顶端
console.log(num2);  // undefined
num = 10;
/*****************************************/
//用 let 或 const 命名的变量有变量提升
console.log(num3); // Uncaught ReferenceError: num3 is not defined
let num3 = 10;      
​
console.log(num4); // Uncaught ReferenceError: num4 is not defined
const num4 = 10;

3.暂时性死区

只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量

var tmp = 123; // 声明
if (true) {
  tmp = 'abc'; // 报错 因为本区域有tmp声明变量
  let tmp; // 绑定if这个块级的作用域 不能出现tmp变量
}

暂时性死区和不能变量提升的意义在于: 为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。

4.重复声明同名变量 和 重新赋值

//var 关键字可以声明同名变量,实际第二次声明是对第一次声明的变量重新赋值
var num1 = 10;
var num1 = 20;
console.log(num1);  // 20
​
//let 和const 关键字不能重复声明同名变量,即使之前是用var声明的也会报错
var num2 = 10;
let num2 = 20;  // Uncaught SyntaxError: Identifier 'num2' has already been declared 
​
//let 和 var 在声明变量时,可以不用初始化
let num3;  
console.log(num3);  // undefined
var num4;  
console.log(num4);  // undefined
​
//const 声明常量时必须初始化,因为 `const` 关键字声明的是常量,声明后不能再赋值
const num5;  // Uncaught SyntaxError: Missing initializer in const declaration
​
//let 声明的变量可以重新赋值
let num1 = 10;
num1 = 20;
console.log(num1);  // 20
//const 只能在声明时赋值,之后不能再重新赋值
const num2 = 10;
num2 = 20;  // Uncaught TypeError: Assignment to constant variable.

5.扩展

        5.1.全局声明

        使用var在全局作用域中声明的变量会成为window对象的属性,letconst声明的变量则不会:

var a = 666;
console.log(window.a); // 666

let b = 666;
console.log(window.b); // undefined

const c = 666;
console.log(window.c); // undefined

        5.2.for 循环中的var 和 let 声明的区别

for (var i = 0; i < 5; i++) {
    setTimeout( () => {
        console.log(i); // 5、5、5、5、5
    }, 0 )
}

for (let i = 0; i < 5; i++) {
    setTimeout( () => {
        console.log(i); // 0、1、2、3、4
    }, 0 )
}

var是因为在退出循环时,迭代变量保存的是导致循环退出的值,也就是 5。在之后异步执行超时逻辑时,所有的i都是同一个变量,因此输出的都是同一个最终值。

而在使用let声明迭代变量时,JS 引擎在后台会为每个迭代循环声明一个新的迭代变量,每个 setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循环执行过程中每个迭代变量的值。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐