JavaScript中let和var的区别

JavaScript中let和var的区别

技术背景

在早期的 JavaScript 中,变量声明主要使用 var 关键字。然而,var 的函数作用域特性在某些情况下会导致代码的复杂性和不可预测性增加,成为 JavaScript 中常见的 bug 来源之一。为了解决这些问题,ECMAScript 6(ES6)引入了 let 关键字,它具有块级作用域,能让变量的作用范围更加精确和可控。

实现步骤

作用域规则

  • var 的函数作用域:使用 var 声明的变量具有函数作用域,即在整个函数体内都可以访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
function run() {
var foo = "Foo";
console.log(foo); // Foo

{
var moo = "Mooo";
console.log(moo); // Mooo
}

console.log(moo); // Mooo
}

run();
  • let 的块级作用域:使用 let 声明的变量具有块级作用域,只能在声明它的块内访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
function run() {
let bar = "Bar";
console.log(bar); // Bar

{
let baz = "Bazz";
console.log(baz); // Bazz
}

console.log(baz); // ReferenceError
}

run();

变量提升

  • var 的变量提升:使用 var 声明的变量会被提升到其作用域的顶部,在声明之前访问变量,其值为 undefined
1
2
3
4
5
6
7
function checkHoisting() {
console.log(foo); // undefined
var foo = "Foo";
console.log(foo); // Foo
}

checkHoisting();
  • let 的变量提升:使用 let 声明的变量也会被提升,但在声明之前访问会导致 ReferenceError,从块的开始到声明语句被处理之前,变量处于“暂时性死区”。
1
2
3
4
5
6
7
function checkHoisting() {
console.log(foo); // ReferenceError
let foo = "Foo";
console.log(foo); // Foo
}

checkHoisting();

创建全局对象属性

  • var 创建全局对象属性:在全局作用域中使用 var 声明的变量会成为全局对象(在浏览器中是 window 对象)的属性。
1
2
var foo = "Foo";
console.log(window.foo); // Foo
  • let 不创建全局对象属性:在全局作用域中使用 let 声明的变量不会成为全局对象的属性。
1
2
let bar = "Bar";
console.log(window.bar); // undefined

变量重声明

  • var 允许重声明:在严格模式下,var 可以在同一作用域内重复声明同一个变量。
1
2
3
'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo1' is replaced with 'foo2'.
  • let 不允许重声明:在同一作用域内,使用 let 重复声明同一个变量会抛出 SyntaxError
1
2
3
'use strict';
let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

核心代码

以下是一个综合示例,展示了 letvar 在不同场景下的区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 作用域和变量提升示例
function scopeAndHoistingExample() {
// var 示例
console.log(varBeforeDeclaration); // undefined
var varBeforeDeclaration = 'var declared later';

// let 示例
// console.log(letBeforeDeclaration); // ReferenceError
let letBeforeDeclaration = 'let declared later';

// 块级作用域示例
if (true) {
var varInBlock = 'var in block';
let letInBlock = 'let in block';
}
console.log(varInBlock); // 'var in block'
// console.log(letInBlock); // ReferenceError

// 全局对象属性示例
var globalVar = 'global var';
let globalLet = 'global let';
console.log(window.globalVar); // 'global var'
console.log(window.globalLet); // undefined

// 变量重声明示例
var varReDeclared = 'first var';
var varReDeclared = 'second var';
console.log(varReDeclared); // 'second var'

// let letReDeclared = 'first let';
// let letReDeclared = 'second let'; // SyntaxError
}

scopeAndHoistingExample();

// 循环闭包示例
function loopClosureExample() {
// var 示例
var varFuncs = [];
for (var i = 0; i < 3; i++) {
varFuncs[i] = function() {
console.log("My value (var): " + i);
};
}
for (var j = 0; j < 3; j++) {
varFuncs[j](); // 输出三次 'My value (var): 3'
}

// let 示例
let letFuncs = [];
for (let k = 0; k < 3; k++) {
letFuncs[k] = function() {
console.log("My value (let): " + k);
};
}
for (let l = 0; l < 3; l++) {
letFuncs[l](); // 依次输出 'My value (let): 0', 'My value (let): 1', 'My value (let): 2'
}
}

loopClosureExample();

最佳实践

  • 优先使用 let:在大多数情况下,建议优先使用 let 来声明变量,因为它的块级作用域可以使代码更具可读性和可维护性,减少因变量作用域问题导致的 bug。
  • 仅在必要时使用 var:如果需要创建全局对象属性或者在函数作用域内需要变量提升的特性,可以考虑使用 var

常见问题

浏览器兼容性问题

let 是 ES6 引入的特性,并非所有浏览器都支持。可以通过以下方式解决:

  • 使用转译工具:如 Babel,将包含 let 的代码转译为 ES5 兼容的代码。
  • 检查浏览器支持情况:在编写代码前,可以参考 Can I Use 网站,了解各浏览器对 let 的支持情况。

闭包问题

在使用 var 时,循环中的闭包可能会导致意外的结果。可以使用 let 来解决这个问题,因为 let 在每次循环迭代时都会创建一个新的变量副本。

1
2
3
4
5
6
7
8
9
10
11
12
13
// var 导致的闭包问题
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i); // 输出三次 3
}, 1000);
}

// 使用 let 解决闭包问题
for (let j = 0; j < 3; j++) {
setTimeout(function() {
console.log(j); // 依次输出 0, 1, 2
}, 1000);
}

JavaScript中let和var的区别
https://119291.xyz/posts/2025-04-16.difference-between-let-and-var-in-javascript/
作者
ww
发布于
2025年4月16日
许可协议