一、为什么你总踩JS数据类型的坑?
你是不是也遇到过:
- 0.1 + 0.2 不等于 0.3?
- 改了obj2,obj1也莫名变了?
- 分不清null和undefined,面试被追问到哑口?
这篇文章帮你彻底搞懂JS的8种数据类型,从底层内存逻辑到实战避坑,全是干货,看完就能用,再也不踩数据类型的坑!

二、JS数据类型总览:8种类型分类
根据ECMA262规范,JS一共有8种数据类型,分为两大阵营:
2.1 原始数据类型(7种)
也叫简单数据类型,内存空间固定,赋值时是拷贝式赋值:
- Number:普通数值
- String:字符串
- Boolean:布尔值
- null:空值(有意设置的空对象引用)
- undefined:未定义(变量未初始化/属性不存在等)
- Symbol(ES6新增):唯一标识符
- BigInt(ES6新增):处理超大数值
2.2 引用数据类型(1种)
- Object:对象(包括数组、函数等),内存空间不固定,赋值时是引用式赋值
三、逐个拆解:每种类型的实战用法+踩坑点
3.1 Number:小心小数精度问题
JS用二进制存储数值,导致小数计算不精确,这是高频踩坑点!
实战代码:
let a = 0.1
let b = 0.2
console.log(a + b) // 输出:0.30000000000000004,不是0.3!
踩坑提醒:
- 金融计算、精度要求高的场景,不要直接用Number做小数运算
- 解决方案:转成整数计算后再转回,或用专门的精度处理库
3.2 null:主动清空值,释放内存
null是「有意设置为空」,核心用途是清空变量、释放内存。
实战代码:
// 定义一个占用大量内存的对象
let largeObject = {
data: new Array(10000000).fill("test")
}
// 手动释放内存:将引用置为null,垃圾回收会清理
largeObject = null
console.log(largeObject) // 输出:null

踩坑提醒:
- null不是「未定义」,是「主动置空」,和undefined本质不同
- typeof null 会返回 “object”,这是JS的历史bug,记住即可
3.3 undefined:默认未初始化状态
undefined表示「本该有值,但目前没有」,常见场景:
实战代码:
// 1. 变量声明未赋值
let a
console.log(a) // 输出:undefined
// 2. 对象不存在的属性
let obj = {}
console.log(obj.age) // 输出:undefined
// 3. 函数无返回值
function noReturn() {}
console.log(noReturn()) // 输出:undefined
// 4. 访问不存在的数组索引
let arr = [1, 2, 3]
console.log(arr[3]) // 输出:undefined
踩坑提醒:
- 不要手动给变量赋值undefined,空值用null更语义化
- 判断变量是否初始化,用 a === undefined 更准确
3.4 Symbol:保证标识符唯一
Symbol是ES6新增,每个Symbol值都是唯一的,解决「属性名冲突」问题。
实战代码:
// 即使标签相同,Symbol也不相等
console.log(Symbol('张三') === Symbol('张三')) // 输出:false
console.log(typeof Symbol('张三')) // 输出:symbol
// 作为对象唯一属性名
let obj = {
[Symbol()]: '唯一值',
prop: '普通属性'
}
console.log(Object.keys(obj)) // 输出:['prop'],Symbol属性不会被常规遍历获取
踩坑提醒:
- Symbol不能用new创建(是原始类型,不是对象)
- Symbol属性不会被for…in、Object.keys遍历,需用Object.getOwnPropertySymbols获取
3.5 BigInt:处理超大数值
Number有取值范围(±2^53-1),超过后精度丢失,BigInt专门解决这个问题。
实战代码:
// 超大整数,加n表示BigInt类型
let num1 = 99999999999999999999999999999999999999999999999999n
let num2 = 123456789098765433467324577654789008733233456899003466788924243n
console.log(num1 + num2) // 正确计算超大数相加
console.log(typeof num1) // 输出:bigint
// 踩坑点:BigInt只能和BigInt运算
console.log(num1 + 1n) // 正确,输出:100000000000000000000000000000000000000000000000000n
// console.log(num1 + 1) // 报错:Cannot mix BigInt and other types
踩坑提醒:
- BigInt和Number不能混合运算,必须统一类型
- BigInt不能用于Math对象的方法(如Math.max)
3.6 Object:引用赋值的坑
对象是引用类型,赋值时复制的是「内存地址」,不是值本身,这是新手高频踩坑点!
实战代码:
let obj = {name: "Alice"}
let obj2 = obj // 复制的是地址,不是新对象
obj2.company = "快手" // 修改obj2,obj也会变
console.log(obj) // 输出:{name: 'Alice', company: '快手'}
console.log(obj2) // 输出:{name: 'Alice', company: '快手'}
// 对比原始类型的拷贝赋值
let a = null
let b = a
b = 2
console.log(a, b) // 输出:null 2,a不受b影响
踩坑提醒:
- 想复制对象但不共享引用,需用浅拷贝(Object.assign/解构)或深拷贝(JSON.parse(JSON.stringify())/专门库)
- 函数参数传对象时,修改参数会影响原对象,注意副作用
四、内存视角:为什么原始类型和引用类型不一样?
理解内存分配,就能彻底搞懂两种类型的差异:
栈内存:存储原始类型
- 空间小、速度快
- 变量直接存值,赋值时拷贝值
- 函数执行完,栈内存会被快速释放
堆内存:存储引用类型
- 空间大、速度稍慢
- 变量存的是「堆内存地址」,赋值时拷贝地址
- 垃圾回收机制会清理无引用的堆内存(如把对象置为null)


