分享这篇文章
Sébastien TIMONER
作为 Web 开发和技术团队管理专家,我专注于创建和优化高性能数字解决方案。通过对 React.js、Node.js、TypeScript、Symfony、Docker 和 FrankenPHP 等现代技术的深入掌握,我确保为各行业企业的复杂 SaaS 项目从设计到生产的成功。
正在为JavaScript变量而烦恼的开发者们!你是否曾经在团队会议中为使用var、let还是const而困惑?别担心,让我们一起轻松愉快地解决这个问题。我们将探索每个关键字,比较它们的作用域(全局、函数、块级),讨论提升(JavaScript的一个有点神奇的特性),提供一些实用的例子,最后还会涉及一些TypeScript的内容。准备好了吗?让我们开始吧!
让我们从var开始,这是JavaScript变量中的"老前辈"。如果JavaScript是一部剧,var就是那个有点老派、不可预测但又有趣的角色。在ES5之前,我们只能用var来声明变量。虽然现在仍然可以使用它,但要注意:它有一些可能会让你惊讶的特性。
var x = 1; var x = 2;
不会触发错误——JavaScript只会用第二个值覆盖第一个值。这在旧代码中很方便,但在新代码中应该避免,因为它可能导致混乱。让我们用一个小例子来说明var过大的作用域:
javascript
是的,即使这个fruit是在if语句中声明的,块外的console.log仍然可以毫无问题地访问它。var不在乎块级边界:它要么是全局的,要么属于包含它的函数,仅此而已。
最后,让我们谈谈提升(hoisting)。在中文中我们有时称之为"提升",但说实话,我们主要使用hoisting这个术语。这是var倾向于在其作用域顶部悄悄声明的特性。JavaScript在执行时,会表现得好像所有的var声明都被"提升"到了函数的顶部。因此,你可以在声明行之前使用变量...不过会得到一个有点奇怪的值。我们稍后在提升部分会详细讨论这个问题(剧透:var喜欢用undefined来让你惊讶)。
总的来说,var为我们提供了很好的服务,但它有一些狡猾的特性:过大的作用域、静默的重复声明、不可预测的提升...我们可以做得更好,幸运的是,ES6引入了新的关键字来整理这一切!
2015年,JavaScript迎来了let(以及const)。这时,我们得到了变量声明的小升级。let就像一个酷酷的、守规矩的年轻人:它修正了var的大部分怪癖,同时保持了灵活性。
let x = 1; let x = 2;
,你会得到一个漂亮的 SyntaxError: Identifier 'x' has already been declared
错误。这避免了很多混乱。让我们把var换成let,清楚地看看作用域的区别:
javascript
使用let,一旦离开if语句的大括号,就结束了。vegetable变量不再被定义。尝试访问它:ReferenceError。这就是let强制执行良好作用域行为的方式。
那么提升呢?let(和const)变量也会被提升,但方式不同。它们在声明前不能使用。从技术上讲,JS引擎知道这个块中有变量(它预留了内存),但在声明行执行之前,任何访问尝试都会触发错误。我们说这些变量处于暂时性死区(是的,听起来像恐怖电影,但这只是"初始化前不可访问"的时髦说法)。我们稍后会详细讨论这个行为,但请记住let不会让你过早地使用变量。
在实践中,let已经成为需要随时间变化的变量的默认选择。它带来了清晰性(块级作用域)和安全性(没有意外的重复声明,没有声明前的访问)。简单来说,let是你声明临时或可变变量的新朋友。
接下来是const!与let同时引入,这个关键字创建常量...或者说几乎是这样。它创建的是引用不会改变的变量。const具有与let相同的块级作用域(不再有var到处游荡)和相同的提升规则(直到声明前的暂时性死区)。最大的区别是,一旦初始化,变量就不能被重新赋值。
const x;
单独使用是不行的(直接SyntaxError)。你必须写 const x = 42;
这样的形式。让我们看一个小例子来清楚地展示与可变变量的区别:
javascript
我们将PI定义为常量,只要不试图改变它,就可以毫无问题地使用它。当我们尝试PI = 3.15时,JavaScript立即阻止了我们:常量不能被修改。
注意:常量并不意味着100%不可变。如果值是对象或数组,你仍然可以修改对象/数组的内部。不能改变的是变量本身(内存中的引用)。例如:
javascript
在这里,尽管使用了const,我们仍然可以更改user对象中的name。但是,完全将user重新赋值为另一个对象是不允许的。所以,const = 常量,不可修改,但仅限于变量本身。不要混淆变量的常量性和值的不变性。
javascript
对于数组也是同样的逻辑:你可以修改其内容(添加、删除、修改元素),但不能将fruits变量重新赋值为一个新数组。把数组想象成一个盒子:你可以改变里面的内容,但不能替换盒子本身。
在实践中,const最适合那些不应该改变的值:比如配置、引用等。它被广泛使用,使代码更加健壮(我们知道这个变量不会改变)。实际上,最佳实践建议默认使用const,只有在知道值需要改变时才切换到let。至于var...嗯,除非有特殊原因,让我们忘记它吧 😉。
让我们更多地讨论作用域。作用域决定了变量在代码中"哪里"可以访问。JavaScript主要有三个作用域级别:
总结:var被限制在函数(或全局)内,而let和const被限制在当前块内。这个区别解决了很多经典问题。例如:
javascript
这里,使用var的循环在外部留下了i,值为3。而使用let的循环在结束时清理了j:之后无法访问。实际上,这意味着你可以在不同的块中使用相同名称的let变量,它们不会相互干扰,而单个var会在这些块之间共享。
让我们谈谈提升。这个声明提升机制经常是混乱的源头(以及会议中的笑话)。提升是JavaScript引擎在执行过程中将变量和函数声明"移动"到它们的作用域顶部的行为。实际上,代码中并没有真正移动什么,这只是JavaScript在执行前预先为变量分配内存。
让我们在实践中测试提升:
javascript
在第一个console.log时,myVar变量已经通过提升在内存中创建,但其默认值是undefined(因为我们还没有赋值)。没有错误,只是显示undefined。然后我们赋值"你好",第二次得到预期的值。
让我们看看let:
javascript
这里,第一个console.log触发ReferenceError。为什么?因为myLet被提升但没有初始化。它在暂时性死区中,无法访问。JavaScript拒绝给我们一个还不真正存在的值。当我们执行 let myLet = "你好";
时,变量离开暂时性死区并获得"你好"。第二个console.log完美工作。
可以清楚地看到:var像忍者一样提升声明并悄悄地给自己undefined值,而let/const采取安全措施,防止在初始化之前使用。教训:避免依赖提升来编码,这是大脑打结(和意外undefined)的配方。最好在作用域顶部声明变量,保持代码整洁。如果忘记了,let/const的行为会用错误提醒你,而var会让你在undefined中挣扎。
啊,顺便说一下,注意传统的函数声明(function myFunction() \{ ... \}
)也会被完全提升。你可以在代码后面调用声明的函数,JavaScript已经知道它了。但是,箭头函数或赋值给变量的函数表达式会根据使用的关键字遵循var/let的提升规则(这是另一天的话题 😉)。
在结束之前,让我们简单谈谈TypeScript。当你开始使用TypeScript(为JavaScript添加静态类型的超集)时,你会遇到我们的三个朋友var、let和const。好消息:它们的作用域和提升行为与纯JavaScript完全相同,因为TypeScript被编译成标准JavaScript。但是,TypeScript在类型方面有自己的特色,特别是对于let和const,值得一看。
let fruit = "苹果";
,TypeScript会推断fruit是string类型。如果你写 let age = 25;
,它会理解age是数字。对于let,它相当宽泛:变量可以改变,所以类型是基本类型(string、number等)。typescript
color变量是"红色"类型,而不仅仅是string。好处?如果稍后代码期望精确的"红色"或"蓝色"值(例如,只接受这些颜色之一的参数),如果你有 const color = "红色"
,你可以毫无顾虑地传递它。使用 let color = "红色"
(宽泛的string类型),TypeScript会抱怨,因为"红色"只是所有字符串中的一个可能性。简单来说,TypeScript的const在有用时允许超精确的类型定义。这就像是一个强化常量概念的奖励。
console.log(myVar); var myVar = 3;
,它会指出你有作用域或顺序问题。对于过早使用let也是如此,TS知道提升规则,会尽可能保护你避免错误。总结一下,TypeScript不会改变var/let/const的基本规则,但会用类型系统的安全网来加强它们。你获得清晰性,更早地捕获错误。记住,特别是TypeScript的const是双重胜利:不可重新赋值的变量和精确的字面量类型——还能要求什么呢?
我们已经涵盖了所有内容。那么,var vs let vs const的胜者是谁?毫不奇怪:在2025年,let & const组合在编写清晰和可维护的代码方面大获全胜。这里有一些实用的建议:
现在你知道在JavaScript中何时使用var、let或const,甚至对TypeScript中的行为也有了一些了解。不再混淆我们的三个朋友,也不会在代码审查中因为毫无理由地使用var而被取笑。去平静地编码吧,记住:尽可能使用const,只在必要时使用let,而var...越少越好!编码愉快!