专栏-null 和 undefined 到底是什么?
很多人第一次学 JavaScript,都会被这两个东西绕住:
javascript
null
undefined
看起来都像"没有值",为什么要搞两个?
我以前一直对这种 none/null/undefined 一类的东西很不理解。
不是因为它多复杂。
而是因为一直没人直接告诉我:
这玩意本质上就是语言提前放好的一个全局标记,一个哨兵。
关键先别把它们想得太神秘。
它们本质上都只是标记值 ,也可以叫哨兵值。
text
正常值
-> 真正的数据
标记值
-> 用来说明这里现在没有正常数据
问题不在于"空不空",而在于:
这个"没有",到底是谁放进去的?
一、null 和 undefined 分别在标记什么?
null 是程序员主动放进去的标记。
比如:
javascript
const user = null;
这不是"还没值"。
而是:
我明确告诉程序,这里现在就是没有正常对象。
所以 null 更像是:
text
人主动写进去的"没有"
而 undefined 不一样。
它通常不是你主动放的,而是 JavaScript 运行过程中自动给出来的结果。
比如:
javascript
let x;
console.log(x); // undefined
javascript
const obj = {};
console.log(obj.age); // undefined
javascript
function f() {}
console.log(f()); // undefined
这些情况的共同点都一样:
这个位置是合法的,但程序最后没有拿到正常值。
所以可以把它压成一句话:
text
null
-> 人主动标记"这里没有值"
undefined
-> 语言自动标记"这里没产生出值"
顺手再补一个最容易混的边界:
undefined 不是"变量根本不存在"。
javascript
let x;
console.log(x); // undefined
这里变量是存在的,只是没值。
但如果你直接访问一个没声明过的变量,通常还是会报 ReferenceError。
二、为什么很多语言只有 null,没有 undefined?
因为很多语言根本不让你去读这种"还没产生值"的位置。
它们的处理方式不是:
text
没值
-> 给你一个特殊值
而是:
text
没值
-> 直接报错
比如在不少语言里:
- 局部变量没初始化就不能读
- 变量没声明就不能用
- 本来该返回结果却没结果,会直接当成错误处理
既然这些情况都已经被错误机制拦住了,那就没必要再额外设计一个 undefined。
所以很多语言最后只保留一种"空标记":
text
null / nil / None
它表示的不是"程序没算出来",而是:
程序允许这里合法地没有对象。
这就是核心区别:
text
很多语言:
主动表示没有
-> null
本来该有值但没产生
-> 直接报错
text
JavaScript:
主动表示没有
-> null
没产生出值
-> undefined
所以 undefined 不是比 null 多了一种"空"。
它只是说明:
JavaScript 选择把"没产生出值"这件事,也做成了一个可以继续传递、继续判断的值。
三、把它当成一种"错误码设计",就更容易想通了
如果到这里还是觉得绕,可以再换一个更实用的角度理解:
说白了,这就是一种错误码设计,只不过它不是放在异常系统里,而是直接放在值系统里。
很多系统都会干这件事。
正常情况返回正常结果。
特殊情况不立刻中断,而是返回一个提前约定好的标记,让你自己继续判断。
比如:
text
查到了
-> 返回真实对象
没查到
-> 返回 null
text
拿到了属性值
-> 返回真实值
这个位置没有值
-> 返回 undefined
从这个角度看,null 和 undefined 其实没有那么特殊。
它们只是语言提前给你准备好的两个状态码。
而且理论上完全可以继续细分。
比如一门语言如果愿意,甚至还可以再造几个专门的标记:
text
参数没传
-> 一个标记
属性不存在
-> 一个标记
函数没返回结果
-> 一个标记
只是 JavaScript 没继续细分,而是把很多场景都压到了 undefined 里。
很多别的语言走的是另一条路:
它们连这个标记都不想给,直接把这些情况判成错误。
所以最后你看到的表面现象才会是:
text
JavaScript
-> null + undefined
很多别的语言
-> 只有 null
但本质上不是 JavaScript 多了一种神秘的空值。
而是它多保留了一类"缺值状态",并把它做成了一个可以继续传递的值。