var与let、const的区别

本文最后更新于:1 年前

var 与 let、const 的区别

什么是变量提升?

JavaScript 中,函数及变量的声明都将被提升到函数的最顶部。
JavaScript 中,变量可以在使用后声明,也就是变量可以先使用再声明

暂时性死区

  • 暂时性死区的本质就是,只要进入当前作用域,所要使用的变量就己经存在,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

在解析代码时,JavaScript 引擎也会注意出现在块后面的 let 声明。只不过在此声明之前不能以任何方式来引用未声明的变量。
在 let 声明之前的执行瞬间被称为“暂时性死区”,在此阶段引用任何后面才声明的变量都会抛出 ReferenceError

1
2
console.log(a) //ReferenceError: a is not defined
let a = 1

全局声明

与 var 关键字不同,使用 let 在全局作用域中声明变量不会成为 windows 对象的属性(var 声明的变量则会编程 windows 对象的属性)

1
2
3
4
5
var name = 'Matt'
console.log(window.name) //'Matt'

let age = 26
console.log(window.age) //undefined

不过 let 声明仍然是在全局作用域中发生的

for 循环中的 let 声明

在 let 出现之前, for 循环定义的迭代变量会渗透到循环体外部:

1
2
3
4
for (var i = 0; i < 5; ++i) {
// 循环逻辑
}
console.log(i) // 5

改成使用 let 之后,这个问题就消失了,因为迭代变量的作用 域仅限于 for 循环块内部:

1
2
3
4
for (let i = 0; i < 5; ++i) {
// 循环逻辑
}
console.log(i) // ReferenceError: i没有定义

在使用 var 的时候,最常见的问题就是对迭代变量的奇特声明 和修改:

1
2
3
4
5
for (var i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0)
}
// 你可能以为会输出0、1、2、3、4
// 实际上会输出5、5、5、5、5

之所以会这样,是因为在退出循环时,迭代变量保存的是导致循环退出的值:5。
在之后执行超时逻辑时,所有的 i 都是同一个变量,因而输出的都是同一个最终值。 而在使用 let 声明迭代变量时,JavaScript 引擎在后台会为每个迭代循环声明一个新的迭代变量。每个 setTimeout 引用的都是不同的变量实例,所以 console.log 输出的是我们期望的值,也就是循环执行过程中每个迭代变量的值。

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

这种每次迭代声明一个独立变量实例的行为适用于所有风格的 for 循环,包括 for-in 和 for-of 循环。
for 循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

1
2
3
4
5
6
7
for (let i = 0; i < 3; i++) {
let i = 'abc'
console.log(i)
}
//abc
//abc
//abc

正确运行以上代码将输出 3 次 abc 。这表明函数内部的变量 i 与循环变量土不在同一个作用域,而是有各自单独的作用域。

一、var 声明变量存在变量提升,let 和 const 不存在变量提升

1
2
3
4
5
6
console.log(a) // undefined  ===>  a已声明还没赋值,默认得到undefined值
var a = 100
console.log(b) // 报错:b is not defined ===> 找不到b这个变量
let b = 10
console.log(c) // 报错:c is not defined ===> 找不到c这个变量
const c = 10

1.const 定义的常量不可以修改,而且必须初始化。

1
const b = 2 //正确// const b;//错误,必须初始化 console.log('函数外const定义b:' + b);//有输出值// b = 5;// console.log('函数外修改const定义b:' + b);//无法输出

2.var 定义的变量可以修改,如果不初始化会输出 undefined,不会报错。

1
2
3
4
5
6
7
8
9
var a = 1
// var a;//不会报错
console.log('函数外var定义a:' + a) //可以输出a=1
function change() {
a = 4
console.log('函数内var定义a:' + a) //可以输出a=4
}
change()
console.log('函数调用后var定义a为函数内部修改值:' + a) //可以输出a=4

3.let 是块级作用域,函数内部使用 let 定义后,对函数外部无影响。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//函数内部使用let定义后,对函数外部无影响
let c = 3
console.log('函数外let定义c:' + c) //输出c=3
function change() {
let c = 6
console.log('函数内let定义c:' + c) //输出c=6
}
change()
console.log('函数调用后let定义c不受函数内部定义影响:' + c) //输出c=3

//嵌套使用相同的标识符不会报错,因为同一个块中没有重复声明
let age = 1
console.log(age) //1
if (true) {
let age = 2
console.log(age) //2
}

再来看这段代码

1
2
3
4
5
6
7
8
9
10
11
function fn() {
//var a
if (true) {
console.log(a + ' now')
} else {
var a = 1
console.log(2)
}
}

fn() // a -> undefined

我们发现不执行的代码也会影响会执行的代码,因为 var a 会提升到 if 语句的前面

undefined 可以翻译为不明确,not defined 可以翻译为未定义

在 Java 中变量的分为全局变量(成员变量)或者局部变量,在方法体中定义的变量都是局部变量,否则是全局变量(即在方法体外,在类中定义的变量)
在 JavaScript 中,在方法体外外用 var 定义的变量其它方法可以共享,在方法中用 var 定义的变量只有该方法内生效。

var 定义变量没有块级作用域的概念,定义变量会被提升到全局和全局变量没有什么区别
let 定义变量有块级作用域,外部无法访问
建议弃用 var,多使用 let 定义变量

二、let、const 都是块级局部变量

let(定义变量,有块级作用域)
const(定义常量,不能被修改)

顾名思义,就是只在当前代码块起作用

1
2
3
4
{
let a = 1
}
console.log(a) // undefined

const 的行为与 let 基本相同,唯一一个重要的区别是用它声明变量时必须同时初始化变量,且尝试修改 const 声明的变量会导致运行时错误。

1)const 声明时候必须赋值

1
const a

编译器报错

1
SyntaxError: Missing initializer in const declaration

2)const 只能进行一次赋值,即声明后不能再修改

1
const a=1 a=2

控制台报错

1
TypeError: Assignment to constant variable.

3)const 如果声明的是复合类型数据(主要是对象和数组),可以修改其属性

const 声明的限制只适用于它指向的变量的引用。换句话说, 如果 const 变量引用的是一个对象,那么修改这个对象内部的属性并不违反 const 的限制。

1
2
const person = {}
person.name = 'Matt' //'Matt'

const 声明一个只读的常量。一旦声明,常量的值就不能改变 。const 实际上保证的并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。
对于简单类型的数据(数值、字符串、布尔值〉而言,值就保存在变量指向的内存地址中,因此等同于常量。
但对于复合类型的数据(主要是对象和数组)而言,变量指向的内存地址保存的只是一个指针, const 只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,这完全不能控制 。 因此,将一个对象声明为常量时必须非常小心。

1
2
3
4
5
6
const obj = {
name: '小明',
age: 2,
}
obj.name = '小红'
console.log(obj)

三、同一作用域下 let 和 const 不能声明同名变量,而 var 可以

1
2
const a = 2
const a = 1

控制台报错

1
Uncaught SyntaxError: Identifier 'a' has already been declared

let 和 const 的区别:

相似:

  • 都是块级作用域
  • 都不能和它所在作用域内的其他变量或函数拥有相同的名称

区别:

  • const 声明的常量必须初始化,而 let 声明的变量不用。
  • const 定义常量的值不能通过再赋值修改,也不能再次声明。
  • 而 let 定义的变量值可以修改。

什么时候用 let,什么时候用 const

定义函数、对象和不变的值用 const

1
2
3
const fun = function () {}
const obj = {}
const PI = 3.24

计算或者需要修改的值用 let

1
2
3
let num1 = 1,
num2 = 2
console.log(num1 + num2)

一般推荐 const 优先,let 次之