Vue 为什么要禁用 undefined?

40,461 阅读9分钟

Halo Word!大家好,我是大家的林语冰(挨踢版)~

今天我们来伪科普一下——Vue 等开源项目为什么要禁用/限用 undefined


敏感话题

我们会讨论几个敏感话题,包括但不限于——

  1. 测不准的 undefined 如何引发复合 BUG?
  2. 薛定谔的 undefined 如何造成二义性?
  3. 未定义的 undefined 为何语义不明?

懂得都懂,不懂关注,日后再说~


1. 测不准的 undefined 如何引发复合 BUG?

一般而言,开源项目对 undefined 的使用有两种保守方案:

  • 禁欲系——能且仅能节制地使用 undefined
  • 绝育系——禁用 undefined

举个粒子,Vue 源码就选择用魔法打败魔法——安排黑科技 void 0 重构 undefined

vue-void.png

事实上,直接使用 undefined 也问题不大,毕竟 undefined 表面上还是比较有安全感的。

readonly-desc.gif

猫眼可见,undefined 是一个鲁棒只读的属性,表面上相当靠谱。

虽然 undefined 自己问题不大,但最大的问题在于使用不慎可能会出 BUG。undefined 到底可能整出什么幺蛾子呢?

你知道的,不同于 null 字面量,undefined 并不恒等于 undefined 原始值,比如说祂可以被“作用域链截胡”。

举个粒子,当 undefined 变身成为 bilibili,同事的内心是崩溃的。

bilibili.png

猫眼可见,写做 undefined 变量,读做 'bilbili' 字符串,这样的代码十分反人类。

这里稍微有点违和感。机智如你可能会灵魂拷问,我们前面不是已经证明了 undefined 是不可赋值的只读属性吗?怎么祂喵地一言不合说变就变,又可以赋值了呢?来骗,来偷袭,不讲码德!

这种灵异现象主要跟变量查找的作用域链机制有关。读写变量会遵循“就近原则”优先匹配,先找到谁就匹配谁,就跟同城约会一样,和樱花妹异地恋的优先级肯定不会太高,所以当前局部作用域的优先级高于全局作用域,于是乎 JS 会优先使用当前非全局同名变量 undefined

换而言之,局部的同名变量 undefined 屏蔽(shadow,AKA“遮蔽”)了全局变量 globalThis.undefined

关于作用域链这种“远亲不如近邻”的机制,吾愿赐名为“作用域链截胡”。倘若你不会搓麻将,你也可以命名为“作用域链抢断”。倘若你不会打篮球,那就叫“作用域链拦截”吧。

globalThis.undefined 确实是只读属性。虽然但是,你们重写非全局的 undefined,跟我 globalThis.undefined 有什么关系?

周树人.gif

我们总以为 undefined 短小精悍,但其实 globalThis.undefined 才能扬长避短。

当我们重新定义了 undefinedundefined 就名不副实——名为 undefined,值为任意值。这可能会在团队协作中引发复合 BUG。

所谓“复合 BUG”指的是,单独的代码可以正常工作,但是多人代码集成就出现问题。

举个粒子,常见的复合 BUG 包括但不限于:

  • 命名冲突,比如说 Vue2 的 Mixin 就有这个瑕疵,所以 Vue3 就引入更加灵活的组合式 API
  • 作用域污染,ESM 模块之前也有全局作用域污染的老毛病,所以社区有 CJS 等模块化的轮子,也有 IIFE 等最佳实践
  • 团队协作,Git 等代码版本管理工具的开发冲突

举个粒子,undefined 也可能造成类似的问题。

complex-bug.png

猫眼可见,双方的代码都问题不大,但放在一起就像水遇见钠一般干柴烈火瞬间爆炸。

这里分享一个小众的冷知识,这样的代码被称为“Jenga Code”(积木代码)。

Jenga 是一种派对益智积木玩具,它的规则是,先把那些小木条堆成一个规则的塔,玩家轮流从下面抽出一块来放在最上面,谁放上之后木塔垮掉了,谁就 GG 了。

jenga.gif

积木代码指的是一点点的代码带来了亿点点的 BUG,一行代码搞崩整个项目,码农一句,可怜焦土。

换而言之,这样的代码对于 JS 运行时是“程序正义”的,对于开发者却并非“结果正义”,违和感拉满,可读性和可为维护性十分“赶人”,同事读完欲哭无泪。

所谓“程序正义”指的是——JS 运行时没有“阳”,不会抛出异常,直接挂掉,浏览器承认你的代码 Bug free,问题不大。

祂敢报错吗?祂不敢。虽然但是,无症状感染也是感染。你敢这么写吗?你不敢。除非忍不住,或者想跑路。

举个粒子,“离离原上谱”的“饭圈倒牛奶”事件——

  • 有人鞠躬尽瘁粮食安全
  • 有人精神饥荒疯狂倒奶

这种行为未必违法,但是背德,每次看到只能无视,毕竟语冰有“傻叉恐惧症”。

“程序正义”不代表“结果正义”,代码能 run 不代表符合“甲方肝虚”,不讲码德可能造成业务上的技术负债,将来要重构优化来还债。所谓“前猫拉屎,后人铲屎”大抵也是如此。

综上所述,要警惕测不准的 undefined 在团队开发中造成复合 BUG。


2. 薛定谔的 undefined 如何造成二义性?

除了复合 BUG,undefined 还可能让代码产生二义性。

代码二义性指的是,同一行代码,可能有不同的语义。

举个粒子,JS 的一些代码解读就可能有歧义。

mistake.png

undefined 也可能造成代码二义性,除了上文的变量名不副实之外,还很可能产生精神分裂的割裂感。

举个粒子,代码中存在两个一龙一猪的 undefined

default.png

猫眼可见,undefined 的值并不相同,我只觉得祂们双标。

undefined 变量之所以是 'bilibili' 字符串,是因为作用域链就近屏蔽,cat 变量之所以是 undefined 原始值,是因为已声明未赋值的变量默认使用 undefined 原始值作为缺省值,所以没有使用局部的 undefined 变量。

倘若上述二义性强度还不够,那我们还可以写出可读性更加逆天的代码。

destruct.png

猫眼可见,undefined 有没有精神分裂我不知道,但我快精神分裂了。

代码二义性还可能与代码的执行环境有关,譬如说一猫一样的代码,在不同的运行时,可能有一龙一猪的结果。

strict-mode.png

猫眼可见,我写你猜,谁都不爱。

大家大约会理直气壮地反驳,我们必不可能写出这样不当人的代码,var 是不可能 var 的,这辈子都不可能 var

问题在于,墨菲定律告诉我们,只要可能有 BUG,就有可能有 BUG。说不定你的猪队友下一秒就给你来个神助攻,毕竟不是每个人都像你如此好学,既关注了我,还给我打 call。

语冰以前也不相信倒牛奶这么“离离原上谱”的事件,但是写做“impossible”,读做“I M possible”。

事实上,大多数教程一般不会刻意教你去写错误的代码,这其实恰恰剥夺了我们犯错的权利。不犯错我们就不会去探究为什么,而对知识点的掌握只停留在表面是什么,很多人知错就改,下次还敢就是因为缺少了试错的成就感和多巴胺,不知道 BUG 的 G 点在哪里,没有形成稳固的情绪记忆。

请相信我,永远写正确的代码本身就是一件不正确的事情,你会看到这期内容就是因为语冰被坑了气不过,才给祂载入日记。

语冰很喜欢的一部神作《七龙珠》里的赛亚人,每次从濒死体验中绝处逢生战斗力就会增量更新,这个设定其实蛮科学的,譬如说我们身边一些“量变到质变”的粒子,包括但不限于:

  • 骨折之后骨头更加坚硬了
  • 健身也是肌肉轻度撕裂后增生
  • 记忆也是不断复习巩固

语冰并不是让大家在物理层面去骨折,而是鼓励大家从 BUG 中学习。私以为大神从来不是没有 BUG,而是 fix 了足够多的 BUG。正如爱迪生所说,我没有失败 999 次,而是成功了 999 次,我成功证明了那些方法完全达咩。

综上所述,undefined 的二义性在于可能产生局部的副作用,一猫一样的代码在不同运行时也可以有一龙一猪的结果,最终导致一千个麻瓜眼中有一千个哈利波特,读码人集体精神分裂。


3. 未定义的 undefined 为何语义不明?

除了可维护性感人的复合 BUG 和可读性感人的代码二义性,undefined 自身的语义也很难把握。

举个粒子,因为太麻烦就全写 undefined 了。

init.png

猫眼可见,原则上允许我们可以无脑地使用 undefined 初始化任何变量,万物皆可 undefined

虽然但是,绝对的光明等于绝对的黑暗,绝对的权力导致绝对的腐败。undefined 的无能恰恰在于祂无所不能,语冰有幸百度了一本书叫《选择的悖论》,这大约也是 undefined 的悖论。

代码是写给人看的,代码的信息越具体明确越好,偏偏 undefined 既模糊又抽象。你知道的,我们接触的大多数资料会告诉我们 undefined 的意义是“未定义/无值”。

虽然但是,准确而无用的观念,终究还是无用的。undefined 的正确打开方式就是无为,使用 undefined 的最佳方式是不使用祂。


免责声明

本文示例代码默认均为 ESM(ECMAScript Module)筑基测评,因为现代化前端开发相对推荐集成 ESM,其他开发环境下的示例会额外注释说明,edge cases 的解释权归大家所有。

今天的《ES6 混合理论》合集就讲到这里啦,我们将在本合集中深度学习若干奇奇怪怪的前端面试题/冷知识,感兴趣的前端爱好者可以关注订阅,也欢迎大家自由言论和留言许愿,共享 BUG,共同内卷。

吾乃前端的虔信徒,传播 BUG 的福音。

我是大家的林语冰,我们一期一会,不散不见,掰掰~