【3分钟速览】如何“严谨地”判断两个变量是否相同

引言

如何“严谨地”判断两个变量相同?仅仅使用 === 就能够了么?html

严格相等

咱们能够很是快的写一个 is 方法来判断变量 x 是否就是 y:react

// 初版
function is(x, y) {
  return x == y;
}

固然,你会很快发现,方法里用了 ==,因为隐式转换的问题,这并不严谨。因此咱们天然会使用以下的方法:git

// 第二版
function is(x, y) {
  return x === y;
}

那么这是否完美了呢?github

一个“更严谨”的方法

// 第三版
function is(x, y) {
  if (x === y) {
    return x !== 0 || y !== 0 || 1 / x === 1 / y;
  } else {
    return x !== x && y !== y;
  }
}

上面方法相较于咱们经常使用的第二版更复杂了。那么为何多了这么多判断呢?redux

下面让咱们来详细看看。编码

1. Infinity

了解 JavaScript 的同窗应该会记得,在全局中有一个叫作 Infinity 的属性,表示数值上的无穷大。spa

Infinity 属性的属性特性
writable false
enumerable false
configurable false

同时,你用 Number.POSITIVE_INFINITY 也能获取到该值。code

于此对应的,也有个 Number.NEGATIVE_INFINITY 的值,实际就是 -Infinityhtm

Infinity 比较特殊的一点在于,在 JavaScript 中 1 / Infinity-1 / Infinity。 被认为是相等的(因为 +0-0,下一节会进一步介绍)blog

而在不少场景中,包括像一些 deepEqual 之类的方法中,咱们不但愿将其断定为相等。学过统计的同窗都知道假设检验中有两类错误

  • I类错误:弃真错误(false positive)
  • II类错误:取伪错误(false negative)

结合咱们上面提到的,第一个条件判断可能就会犯II类错误 —— 1 / Infinity-1 / Infinity 不相同,却判断为相同了。因此须要进一步判断:

x !== 0 || y !== 0 || 1 / x === 1 / y

1 / Infinity-1 / Infinity 在与 0 的相等判断中都会为 true

而其倒数 Infinity-Infinity 是不相等的,因此避免了 1 / Infinity-1 / Infinity 的判断问题。

2. +0-0

其实,上面 Infinity 问题的核心缘由在于于 JavaScript 中存在 +0-0

咱们知道每一个数字都有其对应的二进制编码形式,所以 +0-0 编码是有区别的,平时咱们不主动声明的话,所使用的其实都是 +0,而 JavaScript 为了咱们的运算能更加方便,也作了不少额外工做。

想要更进一步了解 +0-0 能够读一下 JavaScript’s two zeros 这篇文章。

但在不少判断相等的工做上,咱们仍是会把 +0-0 区分开。

x !== 0 || y !== 0 || 1 / x === 1 / y

上面这个式子也就起到了这个做用。

3. NaN

JavaScript 中还有一个叫 NaN 全局属性,用来表示不是一个数字(Not-A-Number)

NaN 属性的属性特性
writable false
enumerable false
configurable false

它有一个特色 —— 本身不等于本身:

这可能会致使判断出现 I 类错误(弃真错误):本来是相同的,却被咱们判断为不相同。

解决的方法也很简单,JavaScript 中只有 NaN 会有“本身不等于本身”的特色。因此只须要判断两个变量是否都“本身不等于本身”便可,即都为 NaN

x !== x && y !== y

若是两个变量都为 NaN,那么他们其实就仍是相同的。

总结

总的来讲,咱们的增强版就是额外处理了 +0/-0NaN 的状况。

实际项目中,不少时候因为并不会碰这样的业务值,或者这些边界状况的判断并不影响业务逻辑,因此使用 === 就足够了。

而在一些开源库中,因为须要更加严谨,因此不少时候就会考虑使用第三版的这类方法。例如在 react-redux 中对 props 和 state 先后相等性判断underscore 中的相等判断方法等。而 underscore 中更进一步还对 nullundefined 作了特殊处理。