有件事儿我记忆特别深。去年我参与前端岗位的终面,问了一道自认为“送分”的基础题,结果十个人里至少五个人答得七零八碎。题目本身很简单——“说说 var、let、const 的区别”。很多人在简历里写“熟练掌握 JavaScript”,可真到了要把这三个关键字讲透的时候,连自己平时代码为什么这么写都说不明白。这不是初级岗位的专利,不少写了三五年业务代码的前端,理解也停留在“var 会提升,let 和 const 不会”这种碎片化层面。
所以我想用一篇尽量贴近实际开发的文章,把这三兄弟的关系彻底梳理一遍——不只是背结论,还要理解语言设计者为什么这样做,以及我们在真实项目里到底应该怎么选。如果你准备面试,这篇文章能让你从“背出区别”升级为“讲透原理”;如果你已经在带团队,那这份内容还能帮你把代码规范落到实际评审里。
1. 从一个让我当场翻车的面试现场说起
1.1 一道变量提升题,为什么我讲不完整
面试时我在白板上写了一行极其简单的代码:
javascript复制console.log(a)
var a = 10
我问:“输出什么?”大部分候选人能答出 undefined,也知道这涉及变量提升。可当我接着问“如果把 var 换成 let 呢”的时候,现场几乎都会沉默一下,然后有人小声说“报错”。
“报什么错?为什么会报这个错?”我再追问,能完整答上来的人就更少了。
这段经历让我意识到,很多前端对 var、let、const 的认知是“背面试题背出来的”,而不是“写代码写出来的”。var 会提升所以输出 undefined,let 不会提升所以报错——这种说法对不对?对,但只有一半。恰恰是缺少的那一半,让很多人进入正规项目之后开始怀疑自己基础不牢。这篇文章我不想只甩出结论,而是把整个演化逻辑穿起来讲。理解了“为什么”之后,很多东西根本不用背。
1.2 var 的三张面孔:函数作用域、提升、重复声明
先说 var 身上的三张面孔,这也是理解它为什么被“嫌弃”的关键。
第一张面孔:var 是函数作用域,而不是块级作用域。
javascript复制function demo() {
var flag = true
if (flag) {
var inner = '可以访问'
}
console.log(inner) // '可以访问'
}
demo()
在很多编程语言的直觉里,if 的大括号应该是一个“块”,块内声明的变量块外不能访问。但在 JavaScript 里,var 没有块级边界,只要在同一个函数内,花括号根本不构成访问边界。这带来的现实问题是什么?变量污染。你本想在 if 里声明一个临时变量,结果整个函数都能改它;如果两个分支里声明了同名变量,相互之间还会干扰。
第二张面孔:var 声明会被提升,但赋值不会。
这是 hoisting 的经典机制。当代码进入函数作用域时,JS 引擎会先把所有 var 声明“提到”作用域顶部,并初始化成 undefined,然后再一句一句执行。所以:
javascript复制console.log(a) // undefined
var a = 10
等价于引擎内部先做了这一步:
javascript复制var a
console.log(a) // undefined
a = 10
第三张面孔:var 允许重复声明。
javascript复制var a = 1
var a = 2
console.log(a) // 2,没有任何报错
这个特性看着方便,实则有毒。在大型项目里,很难保证一个函数内部没有别人定义的同名变量,一旦同名,你的赋值就会悄悄覆盖它的值,而这种覆盖在运行时才会暴露,排查成本不低。
除了这三张面孔,还要补充一个容易忘的细节:在浏览器全局作用域下用 var 声明,会挂到 window 对象上。
javascript复制var globalVar = 'hello'
console.log(window.globalVar) // 'hello'
而 let 和 const 不会:
javascript复制let globalLet = 'world'
console.log(window.globalLet) // undefined
这意味着,用 var 声明的全局变量理论上可以被任何脚本通过 window.globalVar 访问或覆盖,变量名稍有撞车,页面行为就可能失控。对现代工程化项目来说,这是不能接受的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
