变量与数据类型:var、let、const 的区别
说实话,变量声明这块,是每个 JavaScript 初学者都会遇到的坎。我记得我刚入行那会儿,满脑子都是「不就声明个变量吗,有啥好讲的?」结果呢?项目一复杂,各种变量覆盖、作用域混乱,调试到怀疑人生。
今天咱们就把 var、let、const 彻底聊透。顺便把 JavaScript 的几种基本数据类型也捋一遍。
var:老派但坑多
var 是 ES5 时代的产物。它的特点是:函数作用域,不是块级作用域。
什么意思呢?看个例子:
if (true) {
var name = '张三';
}
console.log(name); // 输出 '张三'
你可能会想:「咦?name 不是在 if 块里声明的吗?怎么外面还能访问?」
对,这就是 var 的坑。它没有块级作用域的概念。只有函数才能限制它的作用范围。
另外,var 还有变量提升的特性。说白了,就是声明会被提到作用域顶部,但赋值不会。
console.log(a); // 输出 undefined,不会报错
var a = 10;
嗯,这其实挺反直觉的。我个人习惯是:能不用 var 就不用。除非你在维护老项目。
let:现代 JavaScript 的标配
let 是 ES6 引入的。它解决了 var 的两个核心问题:块级作用域和变量提升的怪异行为。
if (true) {
let age = 25;
console.log(age); // 25
}
console.log(age); // 报错:age is not defined
看到了吧?let 只在花括号内有效。这才是大多数语言该有的样子。
let 也有变量提升,但和 var 不同——它存在「暂时性死区」。也就是说,在声明之前访问变量,会直接报错,而不是 undefined。
console.log(b); // 报错:Cannot access 'b' before initialization
let b = 20;
我个人觉得,let 的设计更符合直觉。你想想看,变量都没声明完,怎么能用呢?
const:声明常量,但没那么「常」
const 也是 ES6 来的。它用来声明常量——一旦赋值,就不能再重新赋值。
const PI = 3.14159;
PI = 3.14; // 报错:Assignment to constant variable
但这里有个容易踩的坑:const 保证的是引用不变,而不是值不变。
什么意思呢?看代码:
const person = { name: '李四' };
person.name = '王五'; // 没问题,可以修改属性
person = {}; // 报错:不能重新赋值
说白了,const 锁的是「变量指向的内存地址」,而不是内存地址里的内容。对于对象和数组来说,内容是可以变的。
我个人习惯是:默认用 const,只有确定需要重新赋值时才用 let。这样代码意图更清晰,也减少意外修改。
三者的对比
| 特性 | var | let | const |
|---|---|---|---|
| 作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
| 变量提升 | 有(初始化为 undefined) | 有(暂时性死区) | 有(暂时性死区) |
| 可重新赋值 | 可以 | 可以 | 不可以 |
| 可重复声明 | 可以 | 不可以 | 不可以 |
| 使用建议 | 尽量不用 | 需要改值时用 | 默认首选 |
基本数据类型
JavaScript 有 7 种基本数据类型(也叫原始类型)。我一个个说。
Number:数字
JavaScript 里的数字不分整数和浮点数,统一用 Number 类型。底层是 IEEE 754 双精度浮点数。
let intNum = 42;
let floatNum = 3.14;
let negative = -10;
let notANumber = NaN; // 非数字
let infinity = Infinity;
这里有个经典问题:0.1 + 0.2 === 0.3 的结果是什么?
答案是 false。因为浮点数精度问题。我在项目中做金额计算时,从来不用浮点数,而是用整数(分)或者专门的库。
String:字符串
字符串可以用单引号、双引号或者反引号(模板字符串)。
let str1 = '单引号';
let str2 = "双引号";
let str3 = `模板字符串,可以嵌入 ${str1}`;
我个人最喜欢模板字符串。为什么?因为拼接字符串太方便了。以前用加号拼接,又丑又容易错。
// 以前
let msg = '你好,' + name + ',你今年' + age + '岁了。';
// 现在
let msg = `你好,${name},你今年${age}岁了。`;
你想想看,哪个更清爽?
Boolean:布尔值
就两个值:true 和 false。但 JavaScript 里有个「真值」和「假值」的概念。
以下值会被当作 false:
- false
- 0
- ''(空字符串)
- null
- undefined
- NaN
其他所有值都是 true。包括空数组 [] 和空对象 {}。
Null 和 Undefined
这两个经常被搞混。我简单说:
- undefined:变量声明了,但没赋值。系统给的默认值。
- null:主动赋值为空。表示「这里什么都没有」。
let a;
console.log(a); // undefined
let b = null;
console.log(b); // null
我个人习惯是:用 null 表示「空」,用 undefined 表示「未定义」。别混着用。
Symbol:独一无二的值
Symbol 是 ES6 新增的。它用来创建独一无二的标识符。
let sym1 = Symbol('描述');
let sym2 = Symbol('描述');
console.log(sym1 === sym2); // false
即使描述一样,两个 Symbol 也不相等。这在做对象属性时特别有用,可以避免属性名冲突。
我在项目中用过 Symbol 来做私有属性。虽然 JavaScript 没有真正的私有,但 Symbol 属性不会被常规方法遍历到,算是一种约定俗成的做法。
总结一下
变量声明这块,记住一个原则:const 优先,let 次之,var 靠边站。
数据类型这块,记住 7 种基本类型:Number、String、Boolean、Null、Undefined、Symbol,还有 ES10 新增的 BigInt(处理超大整数)。
嗯,今天就到这儿。下一章咱们聊运算符和类型转换,到时候再细说那些让人头疼的隐式转换。