在前端面试中,有一个经常遇到的问题:"请你谈谈 var、let 及 const 的区别?"。对于这个问题大多数人都能回答一些,但在细节上却又容易模糊不清。本文将带大家一起理解他们之间的区别。
一、作用域
var
:使用 var 声明的变量具有函数作用域,在整个函数内部都可见。如果在函数内部使用 var 声明的变量,则该变量仅在函数内部有效,在函数外部无法访问。let
和const
:使用 let 和 const 声明的变量具有块级作用域,只在当前代码块内有效。代码块可以是函数、循环或条件语句等。
ES6 引入了块级作用域关键字来解决变量提升带来的问题。使用
let
和const
声明的变量会形成一个封闭的块级作用域,在该作用域中的变量不会影响作用域外部的变量。
javascript
function foo() {
var x = 10
if (true) {
var x = 20
console.log(x) // 输出 20
}
console.log(x) // 输出 20
}
foo()
console.log(x) // ReferenceError: x is not defined
javascript
function foo() {
let x = 10
if (true) {
let x = 20
console.log(x) // 输出 20
}
console.log(x) // 输出 10
}
foo()
console.log(x) // ReferenceError: x is not defined
二、可变性
var
声明的变量可以重新声明,但是let
和const
不可以重新声明。var
和let
声明的变量是可修改 的,可以随时重新赋值。但是 let 声明的变量只能在其作用域范围内重新赋值。- 使用
const
声明的变量是不可变
的,即一旦赋值后就不能再修改 。尝试重新赋值给const
声明的常量会导致错误,但当用const
声明对象时,可以更新对象的属性。
javascript
var say = 'say Hi';
var say = 'say Hello';
let name = 'xiaoming';
let name = 'xiaohua'; // SyntaxError: Identifier 'name' has already been declared
javascript
let say = 'say Hi';
say = 'say Hello';
const user = { name: 'xiaoming' };
user.name = 'xiaohua';
const name = 'xiaoming';
name = 'xiaohua'; // TypeError: Assignment to constant variable.
三、变量提升
变量提升是 JavaScript 中的一种行为,指的是在代码执行之前,变量声明和函数声明会被提升到当前作用域的顶部。
- 函数提升优先于变量提升。
函数声明
会整体地 被提升到作用域顶部,而变量声明
只有声明本身会被提升,赋值操作仍然保留在原位置。 - 对于使用
let
和const
声明的变量,它们也会被提升到它们所在作用域的顶部。但由于存在暂时性死区,如果在变量声明之前访问这些变量,会导致报错。 - 使用
var
声明的变量,在被提升到作用域顶部时会被赋值为undefined
,而使用let
和const
声明的变量不会被初始化。
需要注意的是,如果有多条相同名称的变量声明,只会提升其中的一条声明,后续的声明会被忽略,不会影响已经提升的声明。
javascript
function foo() {
console.log(bar) // function bar() {}
var bar = 1
function bar() {}
}
foo()
由于函数提升优先级大于变量提升,所以 var bar = 1
可以看成 bar = 1
,var 声明可以忽略。
javascript
function foo() {
function bar() {}
console.log(bar)
bar = 1
}
四、暂时性死区
暂时性死区指的是在代码的编译阶段,使用 let
和 const
声明的变量会在当前代码块中创建一个暂时性死区。在声明之前访问这些变量会引发引用错误(案例中的变量 a
)。
当执行到调用还未声明的代码时产生的才会产生错误,编译分析阶段不会报错。
同时,对于其他未声明的变量(案例中的变量 b
和 c
),在当前代码块作用域内部如果引用这些变量,JavaScript 会向上级作用域链查找这些变量。它会一直向上查找,直到找到该变量或者到达全局作用域。
javascript
var a = 1
var b = 2
let c = 3
function foo() {
console.log(b) // 2
console.log(c) // 3
console.log(a) // ReferenceError: Cannot access 'a' before initialization
let a = 11
}
foo()
总结
在使用JavaScript中的变量声明关键字时,需要注意变量提升、块级作用域以及常量声明的限制。了解这些知识点,可以帮助你避免一些常见的错误,有助于编写更健壮和可维护的代码。
往期文章推荐: