本文深入解析 JavaScript 中 var、let 和 const 三种变量声明方式的核心差异。重点阐述 var 的函数级作用域与变量提升特性,以及 let 和 const 引入的块级作用域、暂时性死区和不可重复声明规则。通过代码示例说明 const 对引用类型的行为,并提供现代 JS 开发中优先使用 const 的最佳实践建议。
JavaScript 中的变量采用松散类型机制,可存储任意类型的数据,变量本身仅作为数据的标识符。在 JavaScript 中,声明变量的关键字主要包括 var、let 和 const 三种。
一、var 关键字
使用 var 关键字定义的变量可以赋值任意类型的值。如果声明变量时未进行初始化,该变量的默认值为 undefined。
1.1 函数级作用域
使用 var 声明的变量,其作用域被限制在包含它的函数内部。
function func() {
var a = 'hi'; // 局部变量
}
func();
console.log(a); // ReferenceError: a is not defined
例如,在函数内部使用 var 定义变量 a 并赋值。当函数 func 执行完毕后,该局部变量会被销毁,因此函数外部无法访问变量 a,代码最后一行会抛出变量未定义的错误。
若在函数内部不使用 var 直接给变量 a 赋值,则该变量会提升为全局变量,可在函数外部访问。在浏览器环境中,该变量会成为 window 对象的一个属性。
function func() {
a = 'hi'; // 全局
}
func();
console.log(a); // hi
console.log(window.a); // hi
console.log(window.a === a); // true
1.2 变量提升
使用 var 声明变量时,声明语句会自动提升到当前函数作用域的顶部。
function func() {
console.log(a);
var a = 1;
}
func(); // undefined
上述代码不会报错,而是输出 undefined。这是因为变量声明被提升到了函数作用域顶部,其执行逻辑等价于将声明语句移至函数顶部的代码结构。
function func() {
var a;
console.log(a);
a = 1;
}
func(); // undefined
1.3 重复声明
此外,使用 var 对同一变量进行多次重复声明也是允许的。
function func() {
var a = 1;
var a = 2;
var a = 3;
console.log(a);
}
func(); // 3
1.4 全局变量挂载到 window
在浏览器环境的全局作用域下,使用 var 声明的变量会自动挂载到 window 对象上。
var a = 1;
console.log(window.a === a); // true
一道面试题:
console.log("1":a,b);
var a = 12,b="34";
function foo(){
console.log("2":a,b);
var a=b=12;
console.log("3":a,b);
}
foo()
console.log("4":a,b);
答案: 1 : undefined,undefined //变量已经声明但是未赋值,这里为什么会说变量已经声明了,是因为js编译时会把所有的变量声明到最前面,其次是函数的声明 2: undefined, ‘34’ //因为下面的var a=又把a声明了一次所以,a就又是undefined,注意这里b并没有被声明,如果把var a=b=12改成,var a=12,b=12那么答案就是undefined,undefined 3: 12,12 //a,b均已声明并赋值 4: 12,12 //a,b均已声明并赋值,但是这里要和上一个区分,b=12是因为在函数中重新赋值了。就如第二条所说,如果把var a=b=12改成,var a=12,b=12那么答案就是12,“34”并且这个12是第二行赋值的。
二、let 关键字
let 关键字同样用于声明变量,但其行为与 var 存在显著差异。
2.1 块级作用域
以下代码会报错,因为 let 声明的变量具有块级作用域,即仅在被大括号 {} 包裹的代码块内有效。
if (true) {
let a = 10;
}
console.log(a); // a is not defined
2.2 不可重复声明
以下代码在执行到 let a = 2 时会报错,因为变量 a 在当前块级作用域中已被声明,不允许重复声明。
if (true) {
let a = 1;
let a = 2; // SyntaxError: Identifier 'a' has already been declared
let a = 3;
}
此外,混用 var 和 let 声明同一变量也是不允许的,以下代码均会报错:
let a;
var a; // 报错
var a;
let a; // 报错
2.3 不存在变量提升(暂时性锁区)
使用 let 声明的变量,在声明语句之前无法访问该变量。
if (true) {
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 1;
}
实际上,JavaScript 引擎会识别块级后面的 let 声明,但在声明语句执行前的瞬间,任何对该变量的引用都会导致错误。这一阶段被称为“暂时性死区”(Temporal Dead Zone)。
2.4 全局变量不会挂载到 window
与 var 不同,即使在全局作用域下使用 let 声明变量,该变量也不会挂载到 window 对象上。
var a = 1;
let b = 2;
console.log(window.a === a); // true
console.log(window.b === b); // false
2.5 不依赖条件声明
if (typeof name === 'undefined') {
let name;
}
// name 被限制在 if {} 块的作用域内
name = 'Matt'; // 全局赋值
try {
console.log(age); // 如果 age 没有声明过,则会报错
} catch (error) {
let age;
}
// age 被限制在 catch {}块的作用域内
age = 26; // 全局赋值
三、const 关键字
const 的特性与 let 基本一致,但具有自身的特定规则。
3.1 声明变量时必须同时初始化
以下声明会报错,因为使用 const 声明变量时必须同时提供初始值。
const a; // Missing initializer in const declaration
3.2 不能修改声明后的变量
使用 const 定义变量后,其值不能被重新赋值:
const a = 1;
a = 2; // TypeError: Assignment to constant variable
这里存在一个常见误区:实际上,const 限制修改的是内存地址的引用,而非数据内容本身。
具体规则如下:
- 当 const 定义的常量为基本数据类型时,其值不可被修改。
- 当 const 定义的常量为引用数据类型时,可以通过修改其属性来改变数据内容。
基本数据类型的值直接存储在内存地址中,因此 const 定义的基本类型常量不可改变。而引用数据类型指向的内存地址仅是一个指针,const 锁定的是该指针指向的地址,而非指针所指向的实际数据。因此,通过属性操作可以修改 const 定义的引用类型常量的数据。
例如,使用 const 定义一个数组,虽然不能重新赋值数组变量,但可以通过 push 等方法修改数组内部的数据。
const arr = [];
arr.push(1, 2, 3);
console.log(arr); // [ 1, 2, 3 ]
四、总结及最佳实践
| var | let | const |
|---|---|---|
| 函数级作用域 | 块级作用域 | 块级作用域 |
| 重复声明 | 不可重复声明 | 不可重复声明 |
| 变量提升 | 不存在变量提升 | 不存在变量提升 |
| 值可更改 | 值可更改 | 值不可更改 |
| 全局变量挂载到window | 全局变量不会挂载到window | 全局变量不会挂载到window |
通常,编写 JavaScript 代码时,建议遵循以下原则:
- 不使用var
- const优先,let次之
一道面试题:
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i);
}, 0);
}
答案:6 6 6 6 6
到此这篇关于JavaScrip变量声明关键字var、let、const的文章就介绍到这了,更多相关JS变量声明关键字var、let、const内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!