JavaScript宽松相等(==)中你不知道的秘密

前言

在日常的JavaScript开发中,我们经常使用宽松相等(==)和严格相等(===)来判断两个值是否"相等",但是他们之间有一个很重要的区别,特别是在判断条件上。

那么他们到底有什么区别呢? 我想,很多人都会脱口而出:==比较值相等,===会判断值和类型是否相等。听起来还是挺有道理的,但是还是不够准确。

正确的解释是:==允许在判断相等时进行强制类型转换,而===不允许。

但是你了解==在判断是进行强制类型转换的具体规则吗?在ES5规范中提到了 “抽象相等比较算法”,定义了==运算符的行为。

1. 比较规则

1.1 类型相同之间的相等比较

当==左右两侧的数据类型相同时,直接比较值是否相等。NaN除外,因为NaN是唯一一个自反的数据。

1.2 字符串和数字之间的相等比较

我们现在来举个例子:

      var a = '42'
      var b = 42
      console.log(a == b) // true
      console.log(a === b) // false

想必大家都知道==会输出为true,而=== 会输出为false。因为==发生了强制类型转换,但是你知道是a强制转换成了数字,还是强制类型转换成了字符串呢??

ES5规范中这样定义:

  1. 如果type(x)是数字,type(y)是字符串,则返回x==toNumber(y)的结果。
  2. 如果type(x)是字符串,type(y)是数字,则返回toNumber(x)==y的结果。

所以根据规范,上述例子应该将a的值'42'转换为数字42进行比较。

1.3 其他值与布尔值之间的相等比较

==最容易出错的就是false和true与其他类型值进行比较。

例如:

      var a = '42'
      var b = true
      console.log(a==b) // false

我们都知道'42'为真值,但是为什么会为false呢?

在ES5规范中是这样说的:

  1. 如果type(y)是布尔值,则返回x==toNumber(y)的结果。
  2. 如果type(x)是布尔值,则返回toNumber(x)==y的结果。

所以上诉例子应该将b转换为数字1,此时为字符串和数字的相对比较,根据上述1.2中的规范,将字符串'42'转换为数字42,42与1不等,所以结果为false。

当然,反过来也一样:

      var a = true
      var b = '42'
      console.log(a == b) // false

看到这里,你是否会有一个疑问:为什么"42"既不等于true也不等false?

“42”本身是一个真值(true)没错,其实这个问题本身就是错的。因为这里比较时,不是将"42"转换成布尔值,而是将布尔值转换为对应的数字表示(0或1),所以最后的结果才会是false。

1.3 null与undefined之间的相等比较

在ES5规范中这样说道:

  1. 若x为null,y为undefined,则x==y为true。
  2. 若x为undefined,y为null,则x==y为true。
      var a = null
      var b
      console.log(a == b) // true
      console.log(a == null) // true
      console.log(b == null) // true
      console.log(a == undefined) // true
      console.log(b == undefined) // true

      console.log(a == '') // false
      console.log(b == '') // false
      console.log(b == false) // false
      console.log(b == false) // false

也就是说在==中,null与undefined是一回事。

1.4 对象和非对象之间的相等比较

在ES5中做出如下规定:

  1. 如果type(x)为字符串或数字,type(y)是对象,则返回x == ToPrimitive(y)的结果 
  2. 如果type(x)为对象,type(y)是字符串或数字,则返回ToPrimitive(x) == y的结果 

这里没有提到布尔值,因为遇到布尔值首先会将布尔值转换为数字。

      var a = '42'
      var b = [42]
      console.log(a == b) // true

这里首先b调用ToPrimitive,返回42,然后将'42'转换为数字42,最终为42==42,结果为true。

这里需要注意一下几个情况:

      var a = null
      var b = Object(a)
      console.log(a == b) // false

      var c = undefined
      var d = Object(c)
      console.log(c == d) // false

      var e = NaN
      var f = Object(e)
      console.log(e == f) // false

因为没有对应的封装对象,所以null与undefined无法被封装,Object(null)与Object()均返回一个常规对象。

NaN能够被数字对象封装,而NaN为自反数据,所以拆分之后,返回false。

当然也会有几种特殊的请求,例如:

      console.log(0 == '') // true
      console.log(0 == []) // true
      console.log([] == '') // true

宽松相等如果使用不当,会给我们带来很多烦恼,所以使用宽松相等要遵守以下两个原则,可以为我们避免不必要的错误:

  1. 如果两边的值有true或false,千万不要使用==。
  2. 如果两边的值有[ ]、“”、0,尽量不要使用==。

1.5 抽象关系比较

抽象关系比较中的隐式强制类型转换也许不太引人注目,不过要想成为优秀的JavaScript开发者,应该深入了解一下。

在ES5规范中这样定义:抽象关系比较分为两个部分,比较双方都是字符串和其他情况。

1.5.1 其他情况

 比较双方先调用ToPromitive,如果结果出现非字符串,则调用ToNumber,将双方强制转换为数字进行比较。

例如:

      var a = [42]
      var b = ['43']
      console.log(a < b) // true

如果比较的双方都是字符串,则按字母顺序来进行比较

      var a = ['42']
      var b = ['043']
      console.log(a < b) // false

ToPromitive返回的是字符串,a和b并没有转换成数字,所以比较的是‘42’字符串和‘043’字符串,因为‘0’在‘4’的前面,所以返回false。

那么这个例子会返回什么呢?

      var a = { b: '42' }
      var b = { b: '043' }
      console.log(a < b) // ???

还是false,因为a为[object Object],b也是[object Object],所以a < b 并不成立。

这里要注意了,神奇的事情要发生了,请看下面这个例子:

      var a = { b: '42' }
      var b = { b: '043' }
      console.log(a < b) // false
      console.log(a > b) // false
      console.log(a == b) // false
      console.log(a <= b) // true
      console.log(a >= b) // true

为什么a==b 和 a< b同为false,而a <=b 和a >=b 就为true呢?

因为根据ES5规范,a<=b 被处理为b <a ,然后将结果反转,因为b< a 的结果为false,所以结果为true。

这里为什么a ==b 为true呢? 难道不是同为[object Object]吗?

这里应该按照上面所说的类型相等来判断,如果同为对象,则会判断对象所指向的值是否相等,不发生强制类型转换。

最后

现在我们就已经了解了宽松相等中的一些“坑”,我相信大家在以后的开发中一定不会入这些‘坑’。

现在你是否会有一个疑问:什么时候该用“==”,什么时候用“===”

在《你不知道的JavaScript(中)》一书中提到:

  1. 如果比较两个类型相同的值,则== 和=== 会使用相同的算法,所以除了JavaScript引擎的一些细微区别,他们之间没有任何不同。
  2. 如果比较两个类型不同的值,我们就需要考虑有没有强制类型转换的比较,如果有则使用==,如果没有则使用===,不用在乎性能。

猜你喜欢

转载自blog.csdn.net/qq_44313091/article/details/108197229