lodash源码分析之NaN不是NaN
暗恋之纯粹,在于不求结果,完全把自己锁闭在一个单向的关系里面。
——梁文道《暗恋到偷窥》
本文为读 lodash 源码的第五篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash
gitbook也会同步仓库的更新,gitbook地址:pocket-lodash
本篇分析的是 eq
函数。
作用与用法
eq
函数用来比较两个值是否相等。遵循的是 SameValueZero 规范。
var obj1 = {test: 1}
var obj2 = {test: 1}
var obj3 = obj1
_.eq(1,1) // true
_.eq(+0, -0) // true
_.eq(obj1, obj3) // true
_.eq(obj1, obj2) // false
_.eq(NaN, NaN) // false
几个比较规范
SameValueNonNumber
这个规范规定比较的值 x
和 y
都不为 Number
类型,照抄规范如下:
-
x
的类型不为Number
类型 -
y
的类型与x
的类型一致 - 如果
x
的类型为Undefined
,返回true
- 如果
x
的类型为Null
,返回true
- 如果
x
的类型为String
,并且x
和y
的长度及编码相同,返回true
,否则返回false
- 如果
x
的类型为Boolean
,并且x
和y
同为true
或同为false
,返回true
,否则返回false
- 如果
x
的类型为Symbol
,并且x
和y
具有相同的Symbol
值,返回true
,否则返回false
- 如果
x
和y
指向同一个对象,返回true
, 否则返回false
Strict Equality Comparison
js 中的全等(===
)便是遵循这个规范,照搬规范如下:
- 如果
x
和y
的类型不同,返回false
- 如果
x
的为Number
类型:- a. 如果
x
为NaN
,返回false
- b. 如果
y
为NaN
,返回false
- c. 如果
x
和y
的数值一致,返回true
- d. 如果
x
为+0
并且y
为-0
,返回true
- e. 如果
x
为-0
并且y
为+0
,返回true
- f. 返回
false
- a. 如果
- 按照 SameValueNonNumber 的结果返回
SameValue
规范如下:
- 如果
x
和y
的类型不同,返回false
- 如果
x
的类型为Number
- a. 如果
x
为NaN
并且y
为NaN
,返回true
- b. 如果
x
为+0
并且y
为-0
,返回false
- c. 如果
x
为-0
并且y
为+0
, 返回false
- d. 如果
x
和y
的数值一致,返回true
- e. 返回
false
- a. 如果
- 按照 SameValueNonNumber 的结果返回
SameValueZero
这个是 eq
遵循的规范,如下:
- 如果
x
和y
的类型不同,返回false
- 如果
x
的类型为Number
- a. 如果
x
为NaN
并且y
为NaN
,返回true
- b. 如果
x
为+0
并且y
为-0
,返回true
- c. 如果
x
为-0
并且y
为+0
, 返回true
- d. 如果
x
和y
的数值一致,返回true
- e. 返回
false
- a. 如果
- 按照 SameValueNonNumber 的结果返回
小结:SameValueNonNumber
是基本,Strict Equality Comparison
、SameValue
和 SameValueZero
只是在对待 +0
、-0
和 NaN
上有区别。
源码分析
来看下 eq
的源码:
function eq(value, other) {
return value === other || (value !== value && other !== other)
}
其实eq
的源码其实就只有这么一句。
既然 eq
遵循的是 SameValueZero
规范,那就将源码来拆解一下,看它是怎样符合规范的。
首先,看第一部分:
value === other
就是这么一段,符合的是 Strict Equality Comparison
规范,通过对比可以发现, Strict Equality Comparison
和 SameValueZero
只在对待 NaN
上有区别。
Strict Equality Comparison
规定就算 x
和 y
都为 NaN
时,返回的是 false
, NaN === NaN
返回的就是 false
。但是 SameValueZero
返回的是规定 x
和 y
都为 NaN
时返回的是 true
。因此只需要在 Strict Equality Comparison
的基础上处理 NaN
就可以了。
下面这段便是处理 NaN
的:
(value !== value && other !== other)
在 js 中,只有 NaN
和自身是不相等的,当两个需要比较的值都是和自身不相等时,表明这两个值都为 NaN
,返回 true
。
这样便遵循了 SameValueZero
的比较实现。
可以用Object.is()吗?
Object.is(NaN, NaN)
返回的是 true
,所以 eq
同样可以改成:
function eq(value, other) {
return value === other || Object.is(value, other)
}
Object.is
同样是比较两个值是否一样,但是 Object.is(+0, -0)
返回的是 false
, 它遵循是的 SameValue
规范,因此不可以直接用 Object.is
替代 eq
。
可以用isNaN()吗?
还有个 isNaN
的全局方法,可以用来判断一个值是否为 NaN
。例如 isNaN(NaN)
会返回 true
,那 eq
是否可以改成以下形式呢?
function eq(value, other) {
return value === other || (isNaN(value) && isNaN(other))
}
答案是:不可以!
isNaN
有一个很怪异的行为,如果传入的参数不为 Number
类型,会尝试转换成 Number
类型之后再做是否为 NaN
的判断。所以类似 isNaN('notNaN')
返回的也是 true
,因为字符串 notNaN
会先被转换成 NaN
再做判断,这不是我们想要的结果。
可以用Number.isNaN()吗
为了修复 isNaN
的缺陷,es6
在 Number
对象上扩展了 isNaN
方法,只有是 NaN
时才会返回 true
,因此用 Number.isNaN
来判断是安全的。所以 eq
同样可以改成以下形式:
function eq(value, other) {
return value === other || (Number.isNaN(value) && Number.isNaN(other))
}
参考
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
作者:对角另一面
- 数据库[分库分表]中间件 Sharding-JDBC 源码分析 —— SQL 解析(四)之插入SQL
- 基于sliverlight + wcf的web 文字版IM 示例
- 数据库中间件 Sharding-JDBC 源码分析 —— SQL 解析(三)之查询SQL
- zephir-(11)流程控制语句
- phalcon-入门篇7(Model层基础使用)
- Sharding-JDBC 源码分析 —— SQL 解析(二)之SQL解析
- zephir-(1)开篇介绍
- phalcon-入门篇6(控制器)
- phalcon-入门篇5(请求与返回)
- phalcon-入门篇2(HelloWord与PhalconTools)
- 数据库中间件 Sharding-JDBC 源码分析 —— SQL 解析(一)之语法解析
- phalcon-入门篇4(log日志和session缓存)
- zephir-(5)类型
- zephir-(10)内置函数
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 10W个Java对象有多大
- 一次线程池引发的线上故障分析
- dubbo 启动Failed to save registry store file报错
- 大数据的列式存储格式:Parquet
- springBoot 入门(六)—— 整合Spring框架开启自带的任务调度器执行任务(注解方式)
- java字节流入门(缓冲输出流)
- EsotericSoftware Kryo —— 官方(1)
- Java的I/O类库的基本架构一句话介绍
- 树莓派综合项目1:智能温度测量系统实验
- 一句话ListenableFuture简介
- Google Guava Cache 使用
- Linux下安装maven3.6.2遇到的问题
- Unity3D网络通讯(五)--Socket通讯之Udp通讯
- IP不能作为rocketmq 的topic
- Unity3D网络通讯(六)-- UnityWebRequest实现WebService通讯