1
头图

你从未如此了解const、let与var

引言

ES2015(ES6)推出了许多闪亮的新功能。而letconst却是其中非常亮眼的功能,可用于变量声明。那么它们与var有和不同呢?

内容速递

看了本文您能了解到的知识!

在本文中,我们将讨论varletconst的作用域、用途和变量提升。了解它们的异同,熟练掌握它们的使用。

1、var

ES6出现之前,必须使用var声明。但是var声明的变量引出了很多前端的问题。这也是为什么必须要有新的方法来声明变量。

1.1、var的作用域

JavaScript中,var关键字声明的变量是函数作用域或全局作用域的变量,它的作用域在声明的函数内部或全局范围内。如果在函数外部使用var声明变量,则该变量会成为全局变量。

在函数作用域中,var声明的变量只在声明的函数内部有效,函数内部的变量不会影响函数外部的变量。如果在函数内部重新声明一个同名变量,将会覆盖函数外部的同名变量。

例如

var x = 1;

function test() {
  var x = 2;
  console.log(x); // 输出2
}

test();
console.log(x); // 输出1

以上代码中,x是全局变量,在test函数内部重新声明了一个同名变量x,该变量只在test函数内部有效,不会影响函数外部的变量x

如果在函数内部没有使用var关键字声明变量,而直接使用变量名赋值,相当于在全局作用域中创建了一个变量。

例如

function test() {
  x = 1;
}

test();
console.log(x); // 输出1

以上代码中,在test函数内部没有使用var关键字声明变量x,而直接给x赋值。这相当于在全局作用域中创建了一个变量x,可以在函数外部访问到该变量。

需要注意的是,使用var关键字声明变量时,如果没有使用var关键字直接给变量赋值,会创建一个全局变量,这是一个常见的错误,应该避免。为了避免这种错误,可以使用"use strict"严格模式。在严格模式下,如果没有使用var关键字声明变量,JavaScript会抛出ReferenceError异常。

例如

"use strict";

function test() {
  x = 1; // 抛出ReferenceError异常
}

test();
console.log(x);

1.2、var变量可以重新声明和修改

JavaScript中,使用var关键字声明的变量是可以重新声明和修改的。这是因为var关键字声明的变量是函数作用域或全局作用域的变量,它的作用域在声明的函数内部或全局范围内。

在同一个作用域内,如果使用var关键字重新声明一个已经存在的变量,不会报错,而是会覆盖之前的变量声明。

例如

var x = 1;
var x = 2;
console.log(x); // 输出2

以上代码中,先使用var关键字声明一个变量x并赋值为1,然后再次使用var关键字声明同名变量x并赋值为2,这会覆盖之前的变量声明,最终输出的结果为2

在同一个作用域内,如果直接修改一个变量的值,不会报错,而是会修改变量的值。

例如

var x = 1;
x = 2;
console.log(x); // 输出2

以上代码中,先使用var关键字声明一个变量x并赋值为1,然后直接修改变量x的值为2,最终输出的结果为2

1.3、var的变量提升

JavaScript中,使用var关键字声明的变量具有变量提升(Hoisting)的特性。变量提升是指在当前作用域内,变量声明会被提升到作用域的顶部,因此可以在变量声明之前使用变量。

例如

console.log(x); // 输出undefined
var x = 1;

以上代码中,先输出变量x的值,由于变量提升的特性,变量x的声明被提升到作用域的顶部,因此可以在变量声明之前访问变量x,输出结果为undefined

需要注意的是,只有变量声明被提升,变量赋值不会被提升

例如

console.log(x); // 输出undefined
var x = 1;
console.log(x); // 输出1

以上代码中,先输出变量x的值,由于变量提升的特性,变量x的声明被提升到作用域的顶部,变量赋值不会被提升,因此第一次输出结果为undefined,第二次输出结果为1

在函数作用域中,变量提升的特性也适用。

例如

function test() {
  console.log(x); // 输出undefined
  var x = 1;
  console.log(x); // 输出1
}

test();

以上代码中,在test函数内部使用var关键字声明变量x,并在变量声明之前输出变量x的值,由于变量提升的特性,变量x的声明被提升到函数作用域的顶部,因此可以在变量声明之前访问变量x,输出结果为undefined

1.4、var的问题

var关键字在JavaScript中存在一些问题,主要包括以下几点:

  1. 变量提升问题:使用var关键字声明的变量具有变量提升的特性,变量声明会被提升到作用域的顶部,因此可以在变量声明之前访问变量。这种特性可能会导致代码可读性和可维护性的问题,因为变量的声明位置与变量的使用位置不一致,会使代码难以理解和维护。
  2. 全局变量问题:如果在函数内部使用var关键字声明变量时,忘记使用var关键字或者错误地使用var关键字声明变量,会导致该变量成为全局变量,影响代码的可维护性和可靠性。
  3. 可重复声明问题:使用var关键字声明的变量可以在同一作用域内重复声明,这可能会引起命名冲突和变量覆盖的问题。
  4. 没有块级作用域:使用var关键字声明的变量只具有函数作用域和全局作用域,没有块级作用域。这意味着在块级作用域内声明的变量会泄漏到外部作用域,可能会引起命名冲突和变量覆盖的问题。

2、let

let现在已经成为变量声明的首选。这并不奇怪,因为它是对var声明的改进。

2.1、let的块级作用域

JavaScript中,使用let关键字声明的变量具有块级作用域的特性。块级作用域是指变量在指定的块内可见,而在块外不可见。块级作用域可以是函数、if语句、for语句等代码块。

例如

function test() {
  if (true) {
    let x = 1;
    console.log(x); // 输出1
  }
  console.log(x); // 抛出ReferenceError异常
}

test();

以上代码中,在if语句的代码块中使用let关键字声明变量x,并赋值为1,这个变量只在if语句的代码块中有效。在if语句的代码块外部访问变量x会抛出ReferenceError异常。

2.2、let可以被修改但是不能被重新声明

使用let关键字声明的变量可以被修改但不能重复声明,这是因为变量在块级作用域中具有唯一性

例如

let x = 1;
let x = 2; // 抛出SyntaxError异常

以上代码中,先使用let关键字声明变量x并赋值为1,然后在同一作用域内再次使用let关键字声明同名变量x并赋值为2,这会抛出SyntaxError异常。

需要注意的是,使用let关键字声明的变量在同一作用域内不能重复声明,但可以在不同作用域内声明同名变量。

例如

let x = 1;
if (true) {
  let x = 2;
  console.log(x); // 输出2
}
console.log(x); // 输出1

以上代码中,在全局作用域中声明变量x并赋值为1,然后在if语句的代码块中使用let关键字声明同名变量x并赋值为2,这不会影响全局变量x的值。在if语句的代码块外部,输出变量x的值为1

2.3、let的变量提升

就像var一样,let声明也被提升到作用域顶部。但不同的是:

  • var声明的变量会被提升到其作用域的顶部,并使用undefined值对其进行初始化。
  • let声明的变量会被提升到其作用域的顶部,不会对值进行初始化

因此,如果你尝试在声明前使用let变量,则会收到Reference Error。这是因为let关键字声明的变量具有暂时性死区(TDZ)的特性。暂时性死区是指在当前作用域内,变量从声明到初始化之前无法访问的区域

例如

if (true) {
  console.log(x); // 抛出ReferenceError异常
  let x = 1;
}

以上代码中,在if语句的代码块中先输出变量x的值,由于变量x还没有被声明和初始化,因此会抛出ReferenceError异常。

3、const

const声明的变量保持常量值。

3.1、const的块级作用域

JavaScript中,使用const关键字声明的变量同样具有块级作用域的特性,与let关键字类似。使用const关键字声明的变量也只在指定的块内可见,而在块外不可见。

例如

function test() {
  if (true) {
    const x = 1;
    console.log(x); // 输出1
  }
  console.log(x); // 抛出ReferenceError异常
}

test();

以上代码中,在if语句的代码块中使用const关键字声明变量x,并赋值为1,这个变量只在if语句的代码块中有效。在if语句的代码块外部访问变量x会抛出ReferenceError异常。

3.2、const不能被修改并且不能被重新声明

需要注意的是,使用const关键字声明的变量必须在声明时进行初始化并且初始化后不能再修改变量的值。这是因为const关键字声明的变量是常量,一旦初始化,它的值就不能再被修改。

例如

const x = 1;
x = 2; // 抛出TypeError异常

以上代码中,先使用const关键字声明常量x并赋值为1,然后在同一作用域内尝试修改常量x的值为2,这会抛出TypeError异常。

需要注意的是,使用const关键字声明的变量不能被重新赋值,但是它所引用的对象或数组等复合数据类型的属性或元素是可以被修改的。

例如

const arr = [1, 2, 3];
arr.push(4);
console.log(arr); // 输出[1, 2, 3, 4]

以上代码中,使用const关键字声明常量arr并赋值为数组[1, 2, 3],然后向数组中添加元素4,这不会抛出TypeError异常,因为常量arr所引用的数组本身并没有被重新赋值,只是修改了数组的元素。

在使用const关键字声明复合数据类型的变量时,需要注意变量所引用的对象或数组等是否会被修改。如果需要保证变量所引用的对象或数组等不被修改,可以使用Object.freeze()方法或其他的不可变数据类型来实现。

3.3、const的变量提升

就像let一样,const声明也被提升到顶部,但是没有初始化。使用const关键字声明的变量也具有暂时性死区(TDZ)的特性,与let关键字类似。在当前作用域内,变量从声明到初始化之前无法访问的区域。

4、const、let与var的区别

JavaScript中,constletvar都是用于声明变量的关键字,它们之间有以下几个方面的区别:

  1. 块级作用域constlet关键字都是块级作用域,而var关键字是函数作用域。
  2. 变量提升var关键字具有变量提升的特性,而constlet关键字不具有变量提升的特性。在使用var关键字声明变量时,变量声明会被提升到作用域顶部,因此可以在变量声明之前访问变量;而在使用constlet关键字声明变量时,访问未声明和未初始化的变量会抛出ReferenceError异常。
  3. 重复声明:使用var关键字声明的变量可以在同一作用域内重复声明,而使用constlet关键字声明的变量在同一作用域内不能重复声明。
  4. 赋值:使用const关键字声明的变量必须在声明时进行初始化,并且初始化后不能再修改变量的值;而使用let关键字声明的变量可以在声明后重新赋值。

博客说明与致谢

文章所涉及的部分资料来自互联网整理,其中包含自己个人的总结和看法,分享的目的在于共建社区和巩固自己。

引用的资料如有侵权,请联系本人删除!

感谢勤劳的自己个人博客GitHub,公众号【归子莫】,小程序【子莫说】

如果你感觉对你有帮助的话,不妨给我点赞鼓励一下,好文记得收藏哟!

幸好我在,感谢你来!


归子莫
1k 声望1.2k 粉丝

信息安全工程师,现职前端工程师的全栈开发,三年全栈经验。