🃏 JS 只有 8 种数据类型,但我花了 2 天才搞懂 null 和 undefined 的区别

一、JS 的 8 种数据类型:一份"户口本"

根据 ECMA-262 规范(JS 的"宪法"),JS 一共有 8 种数据类型

1.1 原始数据类型(Primitive)------ 7 种

类型 说明 举例
number 数值 42, 3.14, NaN
string 字符串 'hello', "world"
boolean 布尔值 true, false
null 空值 null
undefined 未定义 undefined
symbol 唯一标识符(ES6 新增) Symbol('foo')
bigint 大整数(ES6 新增) 123n

1.2 复杂数据类型 ------ 1 种

类型 说明 举例
object 对象(包括数组、函数等) {}, [], function(){}

ES6 以前只有 6 种 (number、string、boolean、null、undefined、object),后来加了 symbolbigint,变成了 8 种。

这就像一个村子原来只有 6 户人家,后来搬来了 2 户新邻居,村长重新统计了一下人口,宣布"我们村现在有 8 户了"。


二、null 和 undefined:JS 界的"双胞胎"谜案

2.1 undefined:我还没准备好

老师总结了 undefined 出现的四种场景:

javascript 复制代码
let a; // 未初始化
console.log(a); // undefined

let obj = {}; // 不存在的属性
console.log(obj.name); // undefined

function noReturn() {}
console.log(noReturn()); // 没有返回值的函数

let arr = [1, 2, 3];
console.log(arr[5]); // 访问不存在的数组索引

undefined 的含义是:这个变量/属性/位置"还没被赋值"。 就像一个快递柜,上面写着你的名字,但里面是空的------不是你故意不放东西,是快递还没到。

2.2 null:我故意留空的

再来看 null

javascript 复制代码
let obj = {
    name: 'Alice',
    address: null  // 故意设置为空
}

console.log(obj.address); // null
console.log(obj.age);     // undefined

null 的含义是:这里"应该有个值,但我故意设为空"。 就像你订了一个快递柜,快递到了,但你把东西取出来了,柜子空了------这是你"故意"的行为。

老师还提到一个实际用途------手动释放内存

javascript 复制代码
// 假设有一个超大对象
// let largeObject = {
//     data: new Array(100000000).fill("qxh")
// }
// 用完了,手动回收内存
// largeObject = null;

把变量设为 null,就断开了对对象的引用,垃圾回收器就可以把那块内存回收了。这就像你退租了房子,房东就可以把房子租给别人了。

2.3 一张表分清 null 和 undefined

对比项 null undefined
含义 故意设置为空 还没被赋值
场景 主动清空变量、释放内存 未初始化、不存在的属性、无返回值
态度 "我知道这里需要值,但我选择留空" "我不知道这里需要什么值"
比喻 快递取走了,柜子空了 快递还没到,柜子等着呢

三、内存分配:栈和堆,JS 的"双仓库"系统

3.1 冯诺依曼架构:一切从内存开始

老师从计算机底层讲起:

冯诺依曼架构------运算器、存储器、输入、输出。

代码存在硬盘(外存)里,编译后调入内存执行。执行上下文(变量环境、词法环境)被推入调用栈,就在栈内存中。

3.2 栈内存 vs 堆内存

特性 栈内存(Stack) 堆内存(Heap)
速度 相对慢
空间
存储内容 原始数据类型的值 + 对象的地址 引用数据类型的实际数据
管理方式 函数执行完自动出栈 垃圾回收器管理

老师解释:编译阶段给函数执行上下文分配的栈空间是精确计算的。函数执行完出栈后,栈顶指针偏移一下就切换到下一个上下文。快速、稳定、可扩展。

3.3 拷贝式赋值 vs 引用式赋值

这是今天最重要的知识点之一。看代码:

javascript 复制代码
// 原始数据类型:拷贝式赋值(复印机模式)
let a = null;
let b = a; // 拷贝!b 是 a 的复印件
b = 2;
console.log(a, b); // null, 2 ------ a 不受影响!

// 引用数据类型:引用式赋值(遥控器模式)
let obj1 = {name: "xll"}
let obj2 = obj1; // 引用!obj2 和 obj1 指向同一个对象
obj2.company = "TikTok";
console.log(obj1, obj2); // 两个都多了 company 属性!

原始数据类型是"复印机模式"------你复印一份文件,在复印件上涂涂画画,原件不受影响。

引用数据类型是"遥控器模式"------你和朋友各拿一个遥控器,但控制的是同一台电视。你换了频道,朋友看到的也变了。

这就是为什么修改 obj2.company 会影响 obj1------因为它们指向堆内存中的同一个对象


四、Number:JS 的数学水平,连小学生都不如

4.1 经典名场面:0.1 + 0.2 != 0.3

javascript 复制代码
let a = 0.1;
let b = 0.2;
console.log(a + b); // 0.30000000000000004

没错,JS 算出来的 0.1 + 0.2 不是 0.3,而是一串带尾巴的数字。

老师解释:JS 统一使用二进制来存数值 ,而 0.10.2 在二进制中是无限循环小数(就像十进制里的 1/3 = 0.3333...),存不精确,算出来自然也不精确。

这就像你用"四舍五入"记了一笔账,记了无数次之后,误差累积,最后对不上账了。不是你算错了,是存储方式决定了精度上限。

所以记住:JS 不擅长精确计算。 涉及钱的场景,千万别直接用浮点数加减。

4.2 BigInt:当 Number 不够用的时候

javascript 复制代码
let num1 = 999999999999999999999999999999999999999999999999999999999999999n
let num2 = 123456789098765433467324577654789008733233456899003466788924243n
console.log(num1 + num2, typeof num1); // bigint
console.log(num1 + 1); // 报错!bigint 不能直接和 number 运算

BigInt 是 ES6 新增的数据类型,专门处理超大整数 。用法很简单------在数字后面加个 n

但有个坑:BigIntNumber 不能直接混用运算num1 + 1 会报错,必须写成 num1 + 1n

这就像你开着一辆卡车(BigInt),想和一辆自行车(Number)赛跑,裁判说"你们不是一个量级的,没法比"。


五、Symbol:JS 里的"身份证号"

5.1 每一个 Symbol 都是独一无二的

javascript 复制代码
console.log(Symbol("zzh") === Symbol("zzh")); // false!
console.log(typeof Symbol("zzh")); // symbol

看到没?即使传入相同的参数 "zzh",两个 Symbol 也不相等。

这就像两个人都叫"张三",但身份证号不同------名字可以重复,但身份证号是唯一的。Symbol 就是 JS 世界里的身份证号。

5.2 Symbol 可以做对象的"隐藏属性"

javascript 复制代码
let obj = {
    [Symbol()]: "value",
    prop: "2"
}

Symbol 作为对象的属性名,这个属性就"藏"起来了------普通的 for...inObject.keys() 都遍历不到它。

这就像你在家里藏了一个保险箱,外人翻箱倒柜也找不到------只有拿着对应 Symbol 钥匙的人才能打开。


六、总结:8 种数据类型,每个都有自己的"脾气"

数据类型 分类 脾气 注意点
number 原始 算数不靠谱 0.1 + 0.2 不等于 0.3
string 原始 老实人 没什么坑
boolean 原始 非黑即白 只有 truefalse
null 原始 故意留空 undefined 别搞混
undefined 原始 还没准备好 四种出现场景要记住
symbol 原始(ES6) 独一无二 可做隐藏属性
bigint 原始(ES6) 超大整数 不能和 number 混算
object 引用 共享地址 赋值是"遥控器模式"

理解数据类型,就是理解 JS 的地基。 你不知道地基是什么材料,房子盖得再漂亮,也可能随时塌掉。


写在最后

今天最大的收获,是理解了"栈"和"堆"的区别。以前只知道"引用类型赋值会影响原对象",但不知道为什么。现在知道了:原始类型存值,引用类型存地址

下次面试官问你:"nullundefined 有什么区别?"

你可以淡定地说:

"undefined 是变量声明了但还没赋值,或者访问了不存在的属性;null 是有意设置的一个空值,表示'这里应该有东西,但我选择留空'。null 还可以用来手动释放内存,断开对象引用,帮助垃圾回收。"

然后看着面试官频频点头的样子,心里默念:这波,又稳了。


本文所有代码示例均来自课堂学习资料,真实可运行。

相关推荐
用户938515635076 小时前
从零构建《天龙八部》知识库:EPUB 加载→文本分割→向量嵌入→Milvus 存储→RAG 问答,一条链路打通
javascript·人工智能·全栈
柚yuzumi8 小时前
变量明明定义了,为什么访问不到?深入理解 JavaScript 作用域
javascript
何时梦醒8 小时前
React + TypeScript + Vite 实战:从零构建 Color Picker 应用
前端·javascript·架构
Highcharts.js8 小时前
基于Highcharts开发的OHLC 股票K线图完全指南
javascript·信息可视化·echarts·数据可视化·highcharts·ohlc
默_笙8 小时前
😋 我让 DeepSeek-R1 在浏览器本地跑了起来,后端同事说"你认真的?"
前端·javascript
JasonMa1539 小时前
从零搭建一个微信小程序活动管理平台(一):架构设计与技术选型
javascript·微信小程序·node.js
sugar__salt10 小时前
Vue3 核心特性深度解析:v-model、ref 属性与 nextTick 完全指南
前端·javascript·vue.js·前端框架·vue
古韵10 小时前
你的 uni-app 分页,一个 hook 搞定竞态和加载
javascript·vue.js·uni-app
成都渲染101云渲染666612 小时前
Blender渲染时,纯CPU渲染的设置教程
前端·javascript·blender
多加点辣也没关系12 小时前
JavaScript|第30章:事件机制
开发语言·javascript