ES6 找对象(Vue.js 源码启示录)

重铸前端霸权,吾辈义不容辞!Hello World 大家好,在下大家的林语冰~
Vue.js 源码启示录
最近在看 Vue.js 源码的时候(我也不知道哪来的勇气,反正就是硬看),语冰发现源码中为判断对象特别 DIY 了两个一龙一猪的工具人。Why?


有图有真相,于是乎语冰多想了一层,发现事情并不简单。
对象的正确打开方式
在 ES6 中,我们大约有几种常用的方法可以直接或间接地找对象:
Object.prototype.toString()
typeof
instanceof
Object.prototype.isPrototypeOf()
constructor
Object.prototype.getPrototypeOf()/__proto__
But 上面几种方法可以按需判断不同范围的对象,功能上并不“图灵等价”。
“纯种对象”
举个粒子,我们只想找 Object 构造函数的“直系纯种子代后裔”,即 Object 构造函数的实例对象,我们可以使用 Object.prototype.toString() 方法。

isPlainObject() 工具人找到的是“纯种对象”,譬如说数组等派生类实例对象都会被无视,换而言之,找到的对象必须是“儿子”这一代,不能是“孙子及其后裔”。
BTW,此处的 Reflect.apply() 会自动直接使用 Object.prototype.toSting() 方法,所以 toString() 不是无中生有,也不需要预先声明。实际开发中的最佳实践是在模块作用域中声明变量缓存 toString(),减少全局作用域的查找,防止模块同名变量声明覆盖引发看不见的 BUG。
JSON 兼容对象
举个粒子,我们只想要找 JSON-compliant(JSON 兼容)的对象,我们可以使用 typeof 操作符。

JSON-compliant 对象只能依法序列化 JSONObject(JSON 对象)和 JSONArray(JSON 数组),函数会被“无视”,Set 等对象会被特殊处理。
isJSONObject() 工具人可以拿捏 JSON-compliant 对象,包括但不限于“纯种对象”。换而言之,isJSONObject() 是 isPlainObject() 的超集,isPlainObject() 是 isJSONObject() 的子集。(TypeScript 给语冰彼芯,然后骂骂咧咧退出了直播间......)
BTW,此处语冰的写法与 Vue.js 源码近乎一致,但是大同小异。此处语冰使用的是 == 而非 ===,是因为 == 可以过滤 nullish,从而在短路的时候少判断一次。But Vue.js 考虑到工程化,其编程风格是使用 === 确保语义化和 KISS 原则。
对象全都要
语冰大学舍友的游戏 ID 类似于“九亿少女的梦”,可见其对象之多。倘若我们想找到比 JSON 对象更大范围的对象,譬如说所有对象,我们可以怎么做呢?当然不是给微信二维码啦。
鲁迅先生曾经说过,“在我的后园,可以看见墙外有两株树,一株是枣树,还有一株也是枣树。”——《秋夜》
推而广之,世界上只有“10”种数据类型,要么是原子类型,要么不是原子类型。
举个粒子,有一个比较直男的黑科技就是“二极管思维”——只要不是原子类型,我们就认为祂是引用类型。所以我们可以 DIY 一个判断原始值工具人 isPrimitive(),然后反证法可得,DIY 一个 isRef() 或 isObject() 工具人就可以了。

BTW,此处我们可以简化一下。


实际上 Vue.js 源码中也有 isPrimitive() 工具函数,不过是用 TypeScript 写的,而且考虑到具体刚需没有覆盖所有的基本类型,不过源码整体思路和我们的实现一毛一样。综上所述,我们成功找到所有对象了,但是代码量比较大。
BTW,其实 isPrimitive() 和 isObject() 都可以一行代码优雅解决,大家不妨试一下脑筋急转弯。没有什么刚需是一行代码不能稿定的,倘若有,那就再想一想,或者再码一行。
某前端的无限脑洞
机智如你可能已经要素察觉,为什么前文中语冰只给两个常用的找对象方法拉勾?因为私以为祂们是找对象的最佳实践,而其他方法在实现过程中会有看不见的 BUG 魔鬼在细节。此处我们不妨灵魂拷问一下自己:
instanceof 等方法为何不完备?什么 edge cases(边缘用例)会让 instanceof 等方式无能为力?
isJSONObject() 虽然可以判断 JSON 兼容对象而不报错,但是会把 Set 等 JavaScript 对象“和谐”。倘若只想严格兼容 JSONObject 和 JSONArray 该如何实现?
如何 DIY structuredClone()(结构化克隆算法)支持的对象?
完结撒花
吾乃前端的虔信徒,传播 BUG 的福音。本期前端圆桌就到这里啦,欢迎大家自由言论。我们一期一会,不散不见~
魔法连接目录
[Vue.js 源码](https://github.com/vuejs/core/blob/main/packages/shared/src/index.ts)