JS学习(1)let、const、var的区别

70 阅读5分钟

前言

  • let、const、var的区别,什么是块级作用域,如何用?

在JavaScript中,一共存在3种声明变量的方式:let、const、var。

之所以有3种方式,这是由于历史原因造成的。最初声明变量的关键字就是var,但是为了解决作用域的问题,所以后面新增了let和const的方式。

作用域

ES5中的作用域有:全局作用域、函数作用域,ES6新增了块级作用域。

块级作用域是由{}包裹,if语句和for语句里面的{}也属于块级作用域。

var

  1. 没有块级作用域的概念
// Global Scope
{
  // Block Scope
  var a = 1
}
console.log(a) // 1

上面代码中,在Global Scope(全局作用域)中,且在Block Scope(块级作用域){}中,a输出结果为1,由此可以看出var声明的变量不存在Block Scope的概念。

  1. 有全局作用域和函数作用域
var a = 1
function fn() {
  var a = 2
  console.log(a) // 2
}
fn()
console.log(a) // 1

上面代码中,在Global Scope(全局作用域)中,声明了一个变量a,值为1,在函数fn中,又声明了一个变量a,值为2,所以输出结果为2,在函数fn执行完毕后,输出Global Scope(全局作用域)中的a,输出结果为1。

  1. 不初始化值默认为undefined
var a
console.log(a) // undefined
  1. 存在变量提升
console.log(a) // undefined
var a = 1

上面代码中,在声明变量a之前,就输出a的值,结果为undefined,这说明变量a已经声明了,但是没有赋值,默认为undefined,这就是变量提升。

所谓变量提升,就是将变量的声明部分提升到当前作用域的最顶端

所以上面的代码等价于

var a
console.log(a) // undefined
a = 1
  1. 全局作用域用var声明的变量会挂载到window上
var a = 1
console.log(window.a) // 1
console.log(a) // 1
console.log(this.a) // 1

上面代码中,在Global Scope(全局作用域)中,声明了一个变量a,值为1,然后输出window.a,结果为1,这说明在Global Scope(全局作用域)中,用var声明的变量会挂载到window上。

  1. 在同一个作用域中可以重复声明
var a = 1
var a = 2
console.log(a) // 2

上面代码中,变量a被声明了两次,第二次声明时,变量a的值被覆盖了,所以输出结果为2。

let

  1. 有块级作用域的概念
// Global Scope
{
  // Block Scope
  let a = 1
}
console.log(a) // ReferenceError: a is not defined

上面代码中,在Global Scope(全局作用域)中,且在Block Scope(块级作用域){}中,a输出结果为1,但是当在Global Scope(全局作用域)中输出a时,会报错,说明let声明的变量不存在Global Scope(全局作用域)中。

  1. 没有变量(声明)提升
console.log(a) // ReferenceError: Cannot access 'a' before initialization
let a = 1

上面代码中,在声明变量a之前,就输出a的值,结果为ReferenceError: Cannot access 'a' before initialization,这说明let声明的变量不存在变量提升。

  1. 暂时行死区

当程序的控制流程在新的作用域(module、function或block作用域)进行实例化时,在此作用域中用let/const声明的变量会先在作用域中被创建出来,但因此时还未进行词法绑定,所以是不能被访问的,如果访问就会抛出错误。因此,在这运行流程进入作用域创建变量,到变量可以被访问之间的这一段时间,就称之为暂时死区。

简单理解就是ES6规定,let/const命令会使区块形成封闭的作用域。若在声明之前使用变量,就会报错。

总之,在代码块内,使用let/const命令声明变量之前,该变量都是不可用的。

这在语法上,称为"暂时性死区"(temporal deadzone,简称TDZ)

if (true) {
  // TDZ开始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ结束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}

上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。

  1. 在同一个作用域中不允许重复声明
{
  let a = 1
  let a = 2 // Identifier 'a' has already been declared
}
{
  let a = 1
  var a = 2 // Identifier 'a' has already been declared
}
{
  var a = 1
  let a = 2 // Identifier 'a' has already been declared
}

const

const 跟 let 基本一致,但也有一些区别

  1. 声明时必须初始化
const a // SyntaxError: Missing initializer in const declaration

上面代码中,const命令声明变量a时,没有初始化,就会报错。

  1. 声明后不允许修改
const a = 1
a = 2 // TypeError: Assignment to constant variable.

上面代码中,const命令声明变量a时,初始化为1,然后对a赋值2,就会报错。

总结

  • let、const、var的区别
  • var

    1. 没有块级作用域的概念

    2. 有全局作用域和函数作用域的概念

    3. 不初始化值默认为undefined

    4. 存在变量提升

    5. 全局作用域用var声明的变量会挂载到window上

    6. 在同一个作用域中可以重复声明

  • let

    1. 有块级作用域的概念

    2. 没有变量(声明)提升

    3. 暂时行死区

    4. 不存在全局作用域的概念

    5. 在同一个作用域中不允许重复声明

  • const

    const 跟 let 基本一致,有两点区别

    1. 声明时必须初始化

    2. 声明后不允许修改

  • 什么是块级作用域,如何用

ES5中的作用域有:全局作用域、函数作用域,ES6新增了块级作用域。

块级作用域是由{}包裹,if语句和for语句里面的{}也属于块级作用域

在以前没有块作用域的时候,在if或者for循环中声明的变量会泄露成全局变量,其次就是{}中的内层变量可能会覆盖外层变量。块级作用域的出现解决了这些问题。