作为前端程序员的你,肯定使用过 JS 内置的 Date 对象,也可能像下面这样比较过两个 Date 对象的大小:
new Date('2021-01-10') < new Date('20201-01-11')
返回的结果是 true
。JS 某些特性表现的混乱会让你猜测这是不是巧合,很幸运,在这里不是巧合。
Date 对象确实是可以使用大于、小于、等于这三个比较操作符比较大小的。这时候问题来了,两个 Date 明明是对象,怎么就可以比较大小了呢?今天我们就来解答这个疑惑。
比较操作符有以下几条规则,我们一条条进行解析:
1. 都是数字
这个最简单了,我们直接进行数值比较就好了。
比如:
1 < 2
2. 都是字符串
字符串的规则是逐个比较字符串中每个字符的编码,我们可以使用 String
内置的 charCodeAt
来查看某个字符对应的编码。
"abc".charCodeAt(0)
有个很容易犯的错误,大写的字母会比小写的字符编码小。所以 'Banana' 是小于 'banana',大家在比较字符串大小的时候要把这一点额外考虑进去,如果这一点对你有影响的话,可以考虑把字符串全都小写化,再比较大小。
3. 任意一个是数字
优先把另外一个转为数字,应用规则 1。
比如:
'1' < 2 // Number('1') < 2
4. 任意一个是布尔值
将其转为数值后,应用上面的规则。
true > false // Number(true) > Number(false)
5. 某一个是对象
调用 valueOf
方法,取得的结果应用上面的几条规则进行比较;那如果没有 valueOf
呢?就调用 toString
,取得的结果再按上面的规则进行比较。
比较操作符的规则就是上面这五条。
聪明的你可能发现了,在类型不相同的两个元素进行比较时,可能会包含隐式类型转化,这是个巨大的坑!
比如:'a' < 1
和 'a' > 1
结果都会返回 false
,这是因为我们会先应用规则 3,先想办法把字符串 'a' 转化为了数字类型,得到的结果是 NaN
,而 NaN
和谁比较都会返回 false
。
这一点提醒我们,以后在比较大小时,最好只比较相同类型的变量,以免引发难以察觉到 BUG。 不过在全民 TypeScript 的时代,这一点也可以不用特别注意。
回归主题,我们的 Date 对象为什么能比较大小呢?因为 JS 帮我们重写了它的 valueOf
方法。它返回的结果是 Date 对象的毫秒表示,接下来我们就应用上述的规则 5 就好了。
new Date().valueOf() // 1610266969084
PS: 毫秒表示是当前 Date 对象表示的时间距离 UNIX 纪元(1970 年 1 月 1 日)差了多少毫秒。
new Date('2021-01-10') < new Date('20201-01-11')
// 1610236800000 < 1610323200000
以上,我们就搞懂了为什么 Date 对象可以比较大小。
依照这个原理,如果我们想让在自定义对象中使用比较操作符进行大小的比较,直接重写 valueOf
方法就可以。
class AppleTree {
constructor(age) {
this.age = age
}
valueOf() {
return this.age
}
}
new AppleTree(1) < new AppleTree(10) // true
比较 Date 对象只是个引子,通过它我们搞懂了 JS 中比较操作符的原理,再也不怕在这里踩坑了。
如果本文对你有帮助,欢迎点赞,一个赞就可以让我开心好多天!
希望看的开心。