js 中的 == 与 === - 图1

1. 前言

作为一个程序员,我想大家在第一次看到 a = b 的语句时一定是懵逼的。后来才知道 = 在编程语言中是用来赋值的,而不是用来判断两个值是否相等。

那该怎么判断两个值是否相等呢?在 C 或 java 中,是使用 == 来进行比较,而 JavaScript 就有意思了,除了使用 == , 还加了 ===

2. 区别与选择

2.1 它们有什么区别?

具体的区别就一句话:== 在比较时允许进行强制类型转换,而 === 不允许。

很多人都担心 == 做的事情多一些会不会影响比较的速度,说实话,会的,但影响是微秒级别的,完全可以忽略不计。

不难看出,== 像是 === 的一种更深入的扩展,因此,满足 === 的值一定满足 ==,反之则不成立。

2.2 该如何选择使用它们?

除非你确定你知道 == 之后的结果,否则,尽量使用 === 吧!

3.=== 的比较规则

3.1 基本类型值的比较

=== 的比较规则很简单,对于非对象类型的值,先判断两边的操作数是否是同一类型,如果是,则进行比较,否则,直接返回 false

但有两个例外情况:NaN === NaN+0 === -0

  1. // 不同类型
  2. '12' === 12; // false
  3. 'a' === true; // false
  4. null === '12' // false
  5. null === undefined; // false
  6. // 同类型
  7. 1 === 1; // true
  8. 'a' === 'a'; // true
  9. false === false; // true
  10. null === null; // true
  11. //特殊情况
  12. NaN === NaN; // false
  13. +0 === -0 // true

3.2 引用类型值的比较

对于包含引用类型值的比较,仍然会先判断两边的数据类型,如果只有一个是引用类型值,则直接返回 false,如果两边均是引用类型值,则会比较他们的引用地址是否一致。

  1. const a = {
  2. m: 'string',
  3. n: 12
  4. };
  5. const b = {
  6. m: 'string',
  7. n: 12
  8. };
  9. const c = 12;
  10. a === b; // false
  11. a === c ; // false
  12. const d = a;
  13. a === d; // true

4. == 的比较规则

一开始说过了,在使用 == 进行比较时,运行对两边的操作数进行强制类型转换,那么问题来了,什么情况下会进行转换?不同类型的转换规则又是怎样?

MDN 中有这样一张表,用来展示不同类型值进行 == 判断时的转换规则。

js 中的 == 与 === - 图2

乍一看可能会觉得很乱,但仍然是可以分几种情况来概括这些情况。

在具体分析之前,建议先阅读上一篇文章 显式 (名义) 与 隐式 (鸭子)类型转换
,因为上图中你能看到有诸如 isFalsy()ToNumber()ToString()ToPrimitive 等抽象方法,使用它们只是为了让大家知道强制转换的方向和结果,而这些也都是上一篇文章讲到的内容。

4.1 UndefinedNull 与其它类型值的比较

我们看前两行和前两列可以发现:它们只和自身及对方相等,与其他类型值比较均返回 false ….这个大概就是传说中的「黑风双煞」吧!

大家可能会看到 Object 有一个 isFalse() 方法,这个方法是用来判断参数值是否是假值,这个时候大家可能会有疑问了,对象不是都是真值吗?

但是有例外,比如 document.all 就是一个假值对象,虽然已经被新的 JavaScript 标准废弃,但你或许会在老的项目中看到它,记住就好。

由于这俩值的特殊性,后面我们说“其他类型”值的时候是排除这俩类型的。

4.2 Number 类型值与其他类型值比较

除了上面的黑风双煞,Number 算是相等比较时的大哥,谁想和它比较,谁就得先转成 Number 类型。

4.3 Boolean 类型值与其他类型值比较

既然有大哥,肯定得有小弟,而 Boolean 类型值则一马当先,以身作则,将大哥的原则贯彻到底,堪称模范小弟!

其他类型值想和 Boolean 值做比较,它摇身一变先将自己转成了 Number 类型,哎,你说,别人能怎么办?!只好按照和 Number 类型值比较的原则来了。

无论别人怎么说,Boolean 只想做一只安静的舔狗,无怨无悔,一生一世。

4.4 Object 类型值与其他类型值比较

Object 作为 JavaScript 中最会伪装自己的一种类型,在比较之前谁也摸不透它们的真实身份。也正因为此,它们的日子过得不尽相同。

在进行比较时,JavaScript 会通过 toPrimitive() 方法来揭开他们的真面目,最终你会发现,它们的真实身份可能是任意的一种基本类型。

因此,在最终比较时,它们也将以真实身份与其他类型值比较。

4.5 String 类型值与其他类型值比较

对于 String 类型值来说,在进行比较时的日子并不好过,毕竟,黑风双煞惹不起,黑社会说话也得听,唯一能让它感受到生活希望的,就是在与 Object 这个变色龙进行比较的时候了。

只有 ObjecttoPrimitive() 后转为字符串的时候它们可以以字符串的规则进行比较,否则,它们就要面临黑风双煞或是黑社会。

5. == 正确的使用方法

github 上有位大神总结了下面这张图:

js 中的 == 与 === - 图3

我们可以将此当做一份参考。

其实在实际的使用过程中,只要我们避免一些特殊的情况,== 的使用还是安全的。

下面就是七种所谓的特殊情况。

  1. "0" == false; // true -- 晕!
  2. false == 0; // true -- 晕!
  3. false == ""; // true -- 晕!
  4. false == []; // true -- 晕!
  5. "" == 0; // true -- 晕!
  6. "" == []; // true -- 晕!
  7. 0 == []; // true -- 晕!

如何避免?两个原则:

  1. 如果两边的值中有 true 或者 false,千万不要使用 ==。
  2. 如果两边的值中有 []、”” 或者 0,尽量不要使用 ==。
  3. 全等大法好。

6. 最后

检验大家成果的时候到了,

  1. 仔细想想上面七种特殊情况的产生原因。
  2. 思考下面这些值的比较结果。
  1. [] == ![]; // ?
  2. 2 == [2]; // ?
  3. '' == [null] // ?
  4. Number.prototype.valueOf = function() {
  5. return 3;
  6. };
  7. new Number( 2 ) == 3; // ?