前言
JavaScript 的类型系统是每个前端工程师的必经之路。为什么 [] == ![] 会返回 true?为什么 const 定义的对象可以修改属性?这一切的答案都藏在数据类型的底层存储与转换逻辑中。
一、 JS 数据类型全家桶
JavaScript 目前共有 8 种 内置类型,分为基本数据类型和引用数据类型。
1. 基本数据类型(Primitive)
-
String: 字符串。
-
Number: 数字(含整数、浮点数及
NaN)。 -
Boolean: 逻辑判断位
true/false。 -
Undefined: 变量已声明但未赋值,或函数缺失参数。
-
Null: 表示“空对象”,常用于解除对象引用。
注意:验证
null必须使用全等===,因为null == undefined为 true。 -
BigInt (ES2020) : 安全存储任意精度的整数,解决
Number超过 精度丢失问题。 -
Symbol (ES6) : 表示独一无二的值,常用于对象属性私有化。
2. 引用数据类型(Reference)
- Object: 包含
Array,Date,RegExp,Function等。
二、 内存分配:栈与堆的艺术
基本类型与引用类型的本质区别在于存储位置。
| 特性 | 基本数据类型 | 引用数据类型 |
|---|---|---|
| 存储位置 | 栈 (Stack) | 堆 (Heap) |
| 存储内容 | 直接存储变量值 | 存储指向堆内存的指针(地址) |
| 访问机制 | 直接访问值 | 先取栈中地址,再跳转堆中取值 |
| 复制表现 | 副本独立,互不影响 | 复制地址,指向同一份数据(浅拷贝) |
为什么要分栈和堆?
栈空间连续且小,查找极快;堆空间巨大且灵活。将大小固定的原始值放栈中、大小不定的对象放堆中,是性能与空间的平衡。
三、 显式类型转换(Explicit)
1. 转换为 String
toString(): 注意null和undefined调用此方法会报错。- 与空字符串
+ '': 最简便的方法。
2. 转换为 Boolean
Boolean()包装。- 使用双感叹号
!!:!!'hello' // true。
3. 转换为 Number
Number():null0;undefinedNaN;true1。parseInt/parseFloat: 规则更严格,若首位不是数字,null/undefined/bool均转为NaN。
四、 隐式类型转换:JS 的“黑盒”
隐式转换发生在编译器发现运算符两边数据类型不统一时。
1. 逻辑触发
- 条件语句:
if,while,for中的表达式会被隐式转为 Boolean。 - 逻辑运算符:
&&,||,!会触发转换。
2. 算术触发
-
非加法运算:
-,*,/,%,++,--都会强制将操作数转为 Number。 -
+运算符(最复杂) :- 字符串拼接: 只要有一方是 String,另一方就会转为 String。
- 数值计算: 若双方均为基本类型(且无 String),则转为 Number。
3. 对象转基本类型 (Object to Primitive)
当对象参与运算时,JS 内部会调用 Symbol.toPrimitive、valueOf() 或 toString()。
JavaScript
// 自定义对象转换逻辑
let obj = {
[Symbol.toPrimitive](hint) {
if (hint === 'number') return 10;
if (hint === 'string') return 'hello';
return true;
}
};
console.log(+obj); // 10 (hint 是 number)
console.log(`${obj}`); // "hello" (hint 是 string)
五、 总结与避坑指南
- 判断 null: 永远使用
v === null。 - NaN 的特性:
NaN !== NaN,判断 NaN 请使用Number.isNaN()。 - 引用拷贝: 修改引用类型的变量会影响所有指向该地址的变量,深拷贝(Deep Copy)是解决此问题的利器。