谈谈有趣的[] == []、[] == ![]、{} == !{}

我穿越了,文章写于2019-12-07。

" 刨根问底,就得往祖坟上刨。 "

前言

相信有不少同学曾看到过这道题,而且能够说出正确答案,但是具体问到其判断过程的时候,大多都略有瑕疵(网上不少博文也是如此),那今天就来说说这几个玩意吧。

在此之前你需要对JavaScript基本数据结构储存有大概了解,具体可以略读《你需要知道的JavaScript内存空间知识》。

背景

在JavaScript中,在比较两个数的相等性的时候,存在 == 和 === 两组操作符,即转换类型比较运算符和严格比较运算符。

在使用 == 的时候,会先将操作数转为相同类型,在进行比较。在此过程中就存在我们所说的隐式类型转换。

\[\] == \[\]

答案: false

对于这个比较,了解基本内存分布的同学一眼就看出答案了(类型相同比较内存地址);更稳妥的,我们根据下面这个规则图中第1条第f项来进行判断得出答案。

\[\] == !\[\]

这个比较存在两种操作符:比较操作符和逻辑运算符。

那谁的优先级比较高呢?这个不是我说了算,也不是你说了算,而是MDN说了算(截取一部分)

具体指引详见:
developer.mozilla.org/zh-CN/docs/...

此时先运算逻辑表达式。

对于逻辑非,总结《JavaScript高级程序设计》中的描述,这个操作符会对操作数进行ToBoolean操作,返回一个布尔值。

如果一个数是""、0、null、NaN、undefined,进行(!)操作都会返回true,其余都会返回false。

那么我们可以初步得出:[] == false。

接着看回第一节中的比较规则图,符合其中第8条规则。对右边进行ToNumber转换。

ToNumber转换规则如下:

点击查看源网页

(图片来源于网络)

根据第3行此时我们可以得出:[] == 0。

接着看回第一节中的比较规则图,符合其中第10条规则。对左边进行ToPrimitive转换。所以我们需要比较:ToPrimitive([]) == 0

对ToPrimitive,表面意思为"转换为原始值"。其转换规则如下:

(图片来源于网络)

对于Object类型的描述,看的好像有点懵。不急,查下资料,找到其详细的介绍:

(图片来源于网络)

对于规范的这个详细描述,还是很难理解其想表达的意思。再查下资料,可以将以上内容归纳为:

js 复制代码
JS 引擎内部转换为原始值 ToPrimitive(obj, preferredType) 函数接受两个参数,
第一个 obj 为被转换的对象,
第二个preferredType 为希望转换成的类型(默认为空,接受的值为 Number 或 String )。

在执行 ToPrimitive(obj, preferredType) 时如果第二个参数为空并且 obj 为 Date 的事例时,
此时 preferredType 会被设置为 String ,
其他情况下 preferredType 都会被设置为 Number。

如果 preferredType 为 Number,ToPrimitive 执行过程如下:
1. 如果 obj 为原始值,直接返回;
2. 否则调用 obj.valueOf(),如果执行结果是原始值,返回之;
3. 否则调用 obj.toString(),如果执行结果是原始值,返回之;
4. 否则抛异常。

如果 preferredType 为 String ,将上面的第 2 步和第 3 步调换,即:
1. 如果 obj 为原始值,直接返回;
2. 否则调用 obj.toString(),如果执行结果是原始值,返回之;
3. 否则调用 obj.valueOf(),如果执行结果是原始值,返回之;
4. 否则抛异常。

可以理解为我们在遇到ToPrimitive的时候,只要关心ToPrimitive最终调用的是valueOf方法还是toString方法就行了。

说了这么多,ToPrimitive([]) == 0就可以得出\[\]调用toString之后的结果跟 0 进行比较,即 "" == 0。

说到这基本上结果出来了,根据第一节中的比较规则图,符合第5,6条规则,得到的就是 0 == 0,答案为true。

{} == !{}

如果上面第二节你都搞懂了,那这个比较按照上面的规则来就信手拈来了。

长话短说,执行过程是:

js 复制代码
{} == !{} 
---> {} == false 
---> {} == 0 
---> ToPrimitive({}) == 0 
---> "[Object Object]" == 0 
---> NaN == 0 
---> false

总结

有意思吧?正是存在这种默认隐式类型转换,让我的程序在运行中可能会出现意想不到的bug,所以大家在开发中尽量使用严格相等去做比较运算。

以上就是个人理解这几个玩意的执行步骤及原理,如有发现错误,恳请指正!!!

相关推荐
福兮说5 小时前
前端下载大文件,点了按钮半天没反应:fetch + blob 的三个问题和三种替代写法
前端·javascript·性能优化·文件下载
GreenTea5 小时前
我把 Agent 的 while 循环拆掉了:一种你可能没想到的 Agent 架构
前端·后端·架构
科技观察哨5 小时前
六足平台选型与纳米定位系统集成:HEB-640六自由度位移台在半导体光刻对准中的参数边界与国产替代评估
前端·人工智能
niucloud-admin6 小时前
JAVA V6 多商户商城 开发文档——web前端
前端
颜进强6 小时前
16 · NestJS LifecycleEvents 生命周期事件:五个钩子、三条边界,和 rag-server 的优雅停机
前端·后端·ai编程
今年下半年7 小时前
【VUE】整合腾讯地图、自定义区域边界、村委名称及资产统计(放大显示资产点位)
前端·javascript·vue.js
郑州光合科技余经理7 小时前
同城电商系统:库存变更怎么同步到订单
java·开发语言·前端·后端·uni-app·php·ai编程
我叫黑大帅8 小时前
Go日志库工程选型与逃逸分析评测报告
后端·面试·go
此时不提桶,更待何时9 小时前
05-05-B-对象存储与文件服务面试与生产事故实战
面试·nosql
前端snow9 小时前
ai agent --- DeepAgents 中间件
前端