搞懂 JS 中的比较操作符

464 阅读2分钟

作为前端程序员的你,肯定使用过 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 中比较操作符的原理,再也不怕在这里踩坑了。

如果本文对你有帮助,欢迎点赞,一个赞就可以让我开心好多天!

希望看的开心。