写JavaScript代码时,变量声明用var、let还是const?这仨关键字看着像亲戚,实际“脾气”差别大着呢!想少踩坑、写出更规范的代码,得把它们的区别摸透,今天就唠唠这仨声明方式到底哪里不一样~
var:早期JS里的「自由派」变量
在ES6(2015年)之前,JavaScript里只有var
能声明变量,它就像个“自由派”,规则宽松,却藏着不少容易踩的坑。
作用域:只认“函数”,不认“块”
var
是函数作用域——只要变量在函数里声明,不管被包在if
、for
这些“小括号”(代码块)里,整个函数都能访问它,举个例子:
function testVar() { if (true) { var insideIf = '我在if里'; } console.log(insideIf); // 能拿到“我在if里” }
哪怕insideIf
是在if
块里声明的,函数里其他地方照样能访问,因为var
不认“块”,只认“函数”。
变量提升:声明偷偷“跑”到作用域顶部
var
声明的变量会被“偷偷”提到作用域最顶部,但赋值留在原地,比如这样写:
console.log(a); // 输出undefined,不是报错 var a = 10;
JS引擎会偷偷把它变成这样执行:
var a; // 声明被提升到顶部 console.log(a); // 此时a还没赋值,所以是undefined a = 10; // 赋值留在原来的位置
这种“提升”容易让人误以为变量可以先访问后声明,埋下逻辑隐患。
重复声明:同一个变量能被反复覆盖
同一个作用域里,var
能反复声明同一个变量,后面的会把前面的覆盖掉。
var name = '小明'; var name = '小红'; // 不报错,name变成“小红”
虽然代码能跑,但重复声明很容易让变量值“悄悄被改”,调试时一头雾水。
全局污染:全局变量直接挂到window
上
如果在全局作用域(函数外)用var
声明变量,这个变量会直接挂到window
对象上。
var globalVar = 2024; console.log(window.globalVar); // 输出2024
项目大了,全局变量多了,很容易和其他代码“打架”,引发命名冲突。
let:更规矩的「块级作用域」变量
ES6引入let
后,JavaScript终于有了块级作用域(用包裹的区域,比如if
、for
、函数里的代码块)。let
就像个“守规矩的新人”,把var
的不少坑给填了。
作用域:只在“块”里生效
let
是块级作用域,变量只在自己的“块”里生效,比如在if
块里声明let
变量,出了块就访问不到:
if (true) { let insideIf = '我是let声明的'; console.log(insideIf); // 能拿到 } console.log(insideIf); // 报错:insideIf is not defined
再看循环里的经典场景——用let
声明循环变量,每个循环迭代的变量都是独立的:
for (let i = 0; i < 3; i++) { setTimeout(() => { console.log(i); // 依次输出0、1、2 }, 100); }
如果换成var
,所有定时器会共享同一个i
,最后输出三个3
——这就是var
的函数作用域在循环里挖的坑,let
的块级作用域完美解决了它。
变量提升:有提升,但多了“临时死区”
let
也有提升,但多了个临时死区(TDZ)——变量声明前的区域,访问就报错。
console.log(b); // 报错:ReferenceError: b is not defined let b = 20;
虽然let
的声明被提升了,但JS引擎会“保护”这个变量,在声明前访问就扔错误,逼着你先声明后使用,代码更严谨。
重复声明:同一作用域里不能重复声明
同一个块级作用域里,let
不能重复声明同一个变量。
let age = 18; let age = 20; // 报错:SyntaxError: Identifier 'age' has already been declared
这能避免变量被意外覆盖,减少逻辑错误。
全局挂载:全局声明不挂window
在全局用let
声明变量,不会像var
那样挂到window
上。
let globalLet = '我是let声明的全局变量'; console.log(window.globalLet); // 输出undefined
这样能减少全局命名冲突的风险,让全局作用域更“干净”。
const:「只读」的块级作用域变量
const
和let
一样是块级作用域,但它更像个“固执的守护者”——声明后基本不能变(特殊情况后面讲)。
声明即赋值:必须立刻给初始值
const
声明变量时必须立刻赋值,否则直接报错。
const c; // 报错:SyntaxError: Missing initializer in const declaration
这逼着你在声明时就确定变量的初始值,避免“先声明后赋值”的模糊写法。
基本类型的不可变性
如果const
声明的是字符串、数字、布尔值这些基本类型,赋值后就不能再改。
const num = 100; num = 200; // 报错:TypeError: Assignment to constant variable.
引用类型的「可变」陷阱
如果是引用类型(对象、数组),const
锁的是“变量指向的内存地址”,不是内容。
const person = { name: '张三' }; person.name = '李四'; // 不报错,因为对象内容能改 person = { name: '王五' }; // 报错,因为变量指向的内存地址变了
再比如数组:
const arr = [1, 2, 3]; arr.push(4); // 不报错,数组内容变了 arr = [1, 2, 3, 4]; // 报错,数组的内存地址变了
所以用const
声明引用类型时,要注意“指针不变,内容可变”这个陷阱。
重复声明:同一作用域里不能重复声明
和let
一样,const
在同一作用域里不能重复声明。
const pi = 3.14; const pi = 3.1415; // 报错:SyntaxError: Identifier 'pi' has already been declared
var、let、const核心区别总结
为了更直观,咱们从作用域、变量提升、重复声明、初始化、全局挂载、可修改性这几个维度对比下:
维度 | var | let | const |
---|---|---|---|
作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
变量提升 | 完全提升(声明提升,赋值保留) | 提升但有TDZ(声明前访问报错) | 提升但有TDZ(声明前访问报错) |
重复声明 | 允许(同作用域多次声明,后覆盖前) | 不允许(同作用域重复声明报错) | 不允许(同作用域重复声明报错) |
初始化要求 | 可先声明后赋值 | 可先声明后赋值 | 必须声明时赋值 |
全局挂载 | 全局声明→window 属性 | 全局声明→不挂window | 全局声明→不挂window |
可修改性 | 可随意修改/重复声明覆盖 | 可修改值,不可重复声明 | 基本类型不可改;引用类型指针不可改、内容可改 |
写代码时咋选?看场景!
知道了区别,实际开发选哪个更顺手?给几个实用建议:
优先用const
:变量不需要重新赋值时
当变量不需要重新赋值时(比如配置对象、固定常量、DOM元素引用),用const
更安全,比如定义接口地址、主题配置:
const API_URL = 'https://xxx.com/api'; const themeConfig = { primary: '#ff0000' };
用const
能防止变量被意外赋值,代码可读性也更强(一看就知道这变量不会变)。
再用let
:变量需要重新赋值时
当变量需要在块级作用域内重新赋值时(比如循环变量、条件判断里的临时变量),用let
。
let count = 0; for (let i = 0; i < 5; i++) { count += i; }
循环里的i
用let
保证每个迭代独立;count
需要累加,用let
随时修改。
尽量不用var
:除非维护老项目
除非维护老项目必须兼容(比如公司祖传代码里全是var
),否则现代开发优先let
/const
。var
的函数作用域和变量提升太容易埋雷,比如循环里的闭包问题、全局变量污染,debug时能把人搞疯。
选对声明,少踩坑
把var
、let
、const
的区别吃透,写代码时就能更有“掌控感”,简单说:
var
是早期JS的“老大哥”,自由但容易闯祸;let
是“守规矩的新人”,块级作用域+合理提升,解决了不少var
的坑;const
是“固执的守护者”,让变量在该稳定的地方稳定下来。
日常开发里,能const
就const
,需要变用let
,除非维护老代码,否则和var
说拜拜~这样代码可读性、安全性都能Up!
(偷偷说:现在团队协作开发,基本都要求优先const
/let
,var
已经是“时代的眼泪”啦~)
网友评论文明上网理性发言 已有0人参与
发表评论: