我们为什么需要 undefined?
Halo Word!大家好,我是大家的林语冰(挨踢版)~
欢迎订阅《ES6 混合理论》合集,今天我们来伪科普一道前端面试题——我们为什么需要 undefined
?
面试考点
我们会讨论以下若干面试考点,包括但不限于——
undefined
的设计动机是什么?undefined
和 undeclared 有何异同?undefined
何时可作为缺省值?
懂得都懂,不懂关注,日后再说~
1. undefined
的设计动机是什么?
地球人都知道,JS(JavaScript)中有两个奇葩的“空值”——null
和 undefined
,用于表示“缺少有用的数据值”。
Null 数据类型其实在大多数面向对象语言中蛮常见的。“JS 之父”B.E. 在《JavaScript: The First 20 Years》中曾经说过——null
表示某个预期存在对象值的上下文里“没有对象”。它是根据 Java 的 null 值建模的。
This is arguably consistent with Java where all values are objects and null is essentially the “no object” object.
但是 undefined
呢?Undefined 数据类型的设计动机是什么?
这就需要考虑到 JS 数据类型是否有刚需?类型系统是否完备?
你知道的,JS 有一个都市传说——万物皆对象。不幸的是,这个传说是空穴来风,因为 JS 中的“空”(null
)就不是对象。你可能会质疑语冰说的是错的,那对不起,JS 中的“错”(false
)也不是对象。
事实上,ES 语言规范(ECMAScript Language Specification)曾经说过,JS 中有两大类语言数据类型——
- 对象类型(Object,AKA“复合类型”)
- 原始类型(Primitive,AKA“基本类型”)
你知道的,变量从创建到销毁的过程,我们称之为变量的生命周期(Lifecycle)。
举个粒子,我们来比较一下下对象类型和原始类型的生命周期。
猫眼可见,对象类型和原始类型的操作是对称的——都可以兼容创建和读写操作。
虽然但是,对于销毁操作而言,对象类型和原始类型就并非“众生平等”了。
倘若 JS 真的是万物皆对象,那 null
作为空值其实是恰到好处的,所有值都可以通过对象的空值 null
来销毁。
不幸的是,JS 还有原始类型,那么原始值的“空值”是什么呢?
作为彻头彻尾的完美主义者,为了保证对象类型和原始类型的操作对称性,我们需要 DIY 一个原始类型的“空值”来填坑。
是的没错,机智如你可能已经意识到,这个原始类型的“空值”就是 Undefined 原始类型有且仅有的唯一值——undefined
原始值。
虽然但是,实际上 undefined
并不只是原始类型的“空值”,祂更是所有数据类型中更抽象的“元值”。
《Speaking JavaScript》曾经说过——undefined
有时表示不存在的“元值”。
undefined is also sometimes used as more of a metavalue that indicates nonexistence.
那如何理解“元值”呢?
遇事不决,形而上学。所谓“元”指的是——描述 XX 的 XX。
举个粒子,我们尝试解读几个“元概念”。
- 元宇宙——描述宇宙的宇宙
- 元编程——描述编程的编程
- 元数据——描述数据的数据
- 其他三连可观看内容......
猫眼可见,undefined
作为元值,可以理解为“无中生有”的“无”——即“描述(其他)值的值”。
换而言之,undefined
能够同时兼容原始类型和对象类型。事实上,倘若你愿意,你可以使用 undefined
来达到销毁对象的目的。
虽然但是,出于“类型收窄”和最小授权原则(POLP,AKA“最小权限原则”)考虑,一般我们还是建议使用 null
来表示空对象,因为 null
比 undefined
更加鲁棒且语义化。
语冰很喜欢一首歌叫做《不为谁而作的歌》@林俊杰,undefined
也可以理解为“不为谁而作的值”。
Anyway,undefined
强势加入之后对象类型和原始类型的操作就“众生平等”了,类型系统更加对称且完备。
BTW,集成 undefined
对于早期 JS 的异常处理更友好。
《Speaking JavaScript》曾经说过——请记住,JS 的第一个版本没有异常处理。因此,诸如未初始化变量和缺少属性等异常情况必须通过(特殊)值来表示。
Remember that the first version of JavaScript did not have exception handling. Therefore, exceptional cases such as uninitialized variables and missing properties had to be indicated via a value.
举个粒子,异常处理可以通过类型转换的特殊值来表示。
猫眼可见,null
和 undefined
数字化的结果不能说是一猫一样,只能说是一龙一猪。
虽然但是,null
也可以类型转换,那为什么不能使用 null
来表示异常呢?难道“JS 之父”B.E. 对 undefined
一心一意,就像语冰对猫猫一样纯情?
原因在于 null
存在两个方面的局限性:
null
通常用来表示对象类型,不够“海王”,无法体现表示原始类型异常的语义;null
被数字化为0
而非回头率更高的哨兵值NaN
,这样错误更难被发现。
由于借鉴 Java 的 null
设计并不足以满足上述条件,于是乎,“JS 之父”B.E. 就一拍脑袋,小手一抖,集成了 undefined
原始值。
“技术沙漠”限制了我们的挨踢想象力,没想到一个小小的 undefined
背后还有比洋葱还洋葱的层层博弈!语冰以前总觉得 undefined
要么是 BUG,要么是历史包袱,要么既是 BUG 又是历史包袱,原来这是因为我在地下室,而“JS 之父”在大气层,那没事了......
怪不得“JS 之父” 10 天就能创造一个编程语言,而语冰 10 天连 JS 都没整明白。今天总算顿悟了,误会解除,可以摘下 undefined
限定的有色眼镜了。
综上所述,undefined
的设计动机(起码)有两大原因:
undefined
使 JS 的类型系统更加完备;undefined
在早期对异常处理更友好。
2. undefined
和 undeclared 有何异同?
地球人都知道,undefined
一般被翻译为“未定义”。虽然但是,undefined
is not "not defined".(“未定义”不是“未定义”。)
你知道的,如何定义“定义”本身就很暧昧。定义是指“未声明”还是指“已声明但未赋值”呢?
想当年语冰就常常把 undefined
和 undeclared 混淆。
举个粒子,当我们尝试读写一个未声明的变量时,就会抛出异常。
猫眼可见,undeclared(未声明)报错信息是“not defined”(未定义),很容易让新手感到困惑。
更离谱的是,对于 typeof
操作符而言,undefined
和 undeclared 的行为不能说是一龙一猪,只能说是一猫一样。
举个粒子,当我们使用 typeof
时,这两者的行为就神同步了。
猫眼可见,typeof
不仅能对 undefined
进行“人脸识别”,而且还能兼容未声明的变量。一般而言,读写未声明的变量是会抛出 ReferenceError 的,但是 typeof
的“透明处理”直接将异常“吞并”了。
typeof
的“众生平等”就让 undefined
和 undeclared 的界限更模糊了。
综上所述,undefined
的“未定义”应该理解为“已声明未赋值”。undefined
和 undeclared 的异同点如下:
undefined | undeclared | |
---|---|---|
语义 | not assigned | not defined |
理解 | 已声明/未赋值 | 未声明/未定义 |
作用域查找 | undefined 原始值 | ReferenceError |
typeof | 'undefined' | 'undefined' |
3. undefined
何时可作为缺省值?
ECMAScript 语言规范曾经说过——Undefined 类型有且仅有一个值叫做 undefined
。任何未赋值的变量(默认)拥有 undefined
原始值。
The Undefined type has exactly one value, called undefined. Any variable that has not been assigned a value has the value undefined.
倘若你对“未定义”感到困惑,你可以考虑将 undefined
理解为变量已声明未赋值的缺省值——即可以缺少/省略的值。
举个粒子,下列情况变量会自动初始化 undefined
原始值作为缺省值。
猫眼可见,上述情况都会自动使用 undefined
原始值作为缺省值,一般而言我们不需要显式将变量赋值为 undefined
。
语冰有幸拜读一部作品叫做《看不见的大猩猩》,undefined
也可以理解为“看不见的缺省值”。
免责声明
本文示例代码默认均为 ESM(ECMAScript Module)筑基测评,因为现代化前端开发相对推荐集成 ESM,其他开发环境下的示例会额外注释说明,edge cases 的解释权归大家所有。
今天的《ES6 混合理论》就讲到这里啦,我们将在本合集中深度学习若干奇奇怪怪的前端面试题/冷知识,感兴趣的前端爱好者可以关注订阅,也欢迎大家自由言论和留言许愿,共享 BUG,共同内卷。
吾乃前端的虔信徒,传播 BUG 的福音。
我是大家的林语冰,我们一期一会,不散不见,掰掰~
转载自:https://juejin.cn/post/7241504677912444986