==和===你真的会用吗?

653 阅读6分钟

平时在代码中我们会发现有时候用“==”有时候是“===”,那么什么时候用“==”什么时候用“===”呢?它们的区别是什么呢?欲知详情,请看下文。

在说到“==”和“===”的时候就不得不说到JavaScript中的 宽松相等(loose equals)严格相等(strict equals)

在一些博客或者书籍中可能对两者的解释是:==检查值是否相等;===检查值与类型是否相等。但这还不是最准确的,因为如果是这样,那怎么解释[]==![]呢?事实上最正确的解释是:

宽松相等(==):允许在相等比较中进行强制类型转换;

严格相等(===):不允许在相等比较中进行强制类型转换。

事实上无论是==还是===,在进行相等比较的时候都会检查操作数的类型,只是它们对操作数类型不同时的处理方式不同而已。

我们说看问题要看到其本质,分析事物要全面,要想做到全面就要分情况讨论问题。所以,在说数值是否相等的时候,我们还要从不同的值类型来分析问题。因为===是严格相等,较容易理解,下面我们主要讨论的是==的几种情况。

字符串与数字

我们先来看一段代码:

var a = 21;
var b = "21";
a===b;//false
a==b;//true

我们可以说在“a===b”时,之所以是false是因为a与b的类型不同,而===又不允许强制类型转换,所以当然是false了。而==中a与b之间发生了强制类型转换,所以会有一个操作数被转换为另一个操作数的相同类型,所以是true。

那么到底是那个操作数发生转换呢?判断的依据又是什么呢?

在ES5规范中的定义是这样的:

(1)如果Type(x)是数字,Type(y)是字符串,则返回x==ToNumber(y)的结果。

(2)如果Type(x)是字符串,Type(y)是数字,则返回ToNumber(x)==y的结果。

所以根据规范,“21”会被ToNumber强制类型转换为数字21,如此,a==b就变成了21==21。结果就是true没问题。

其他类型与布尔值

我们知道对于“相等”概念,最常见的就是条件难判断表达式的使用,我们往往需要根据条件判断是否成立来决定下一步要执行的代码。而条件判断里面最多的就是相等比较成立与否。

在这里我们引入JavaScript中的“假值”概念,什么是“假值”呢?在JavaScript中可以将数值分为两种:

(1)可以被强制类型转换为false的值;

(2)被强制类型转换为true的值;

而且对于(1)值只占JavaScript值的少部分。 分别是:

  • undefined
  • null
  • false
  • +0,-0,NaN
  • ""

除此之外的值都可以说是“真值”,而且在 JavaScript中所有的对象都是真值

有了上述的概念我们再来看一段代码:

var a = "21";
var b = true;
a==b;//false

是不是觉得有些奇怪?按道理来说“21”不是一个真值吗?为什么结果不是true呢?其实还要和ES5中的规范说起:

(1)如果Type(x)是布尔类型,则返回ToNumber(x)==y的结果。

(2)如果Type(y)是布尔类型,则返回x==ToNumber(y)的结果。

所以由此看来对于上面的例子就是,true会变成1,"21"==1,两者的类型不相同,就还要发生强制类型转换,"21"会被转换为21,那么最后就会变成21==1,结果当然是false。

值的注意的是这里的“相等”返回的布尔值不代表说这个数值的类型判断,什么意思呢?来看个例子:

var a = "21";
var b = false;
a==b;//false

对比上面的结果是不是很奇怪?"21"怎么既不是true,也不是false了?事实上,"21"是一个真值没错,但是"21"==true/false中并没有发生布尔值的比较和强制类型转换。不是"21"转为true/false,而是"21"==>21;true/false==>1/0;最后比较的是21与1或者0是否相等.

所以在写条件判断语句的时候:

var a ="21";
//不要这样写,条件判断不成立的
if(a==true){};
//这样也不对的
if (a===true){};
//这样会好一些
if(a){};
//这样也行
if(!!a){};
//或者这样也行
if(Boolean(a)){};

所以在写代码的时候尽量避免==true/false这种情况出现。

Null和Undefined的相等比较

前面说过null和undefined都是JavaScript中的“假值”,那么是不是“假值”之间的相等比较就一定成立呢?其实不然!

在ES5规范中写道:

(1)如果x为null,y为undefined,则结果为true.

(2)如果x为undefined,y为null,则结果为true

也就是说在==的情况下null和undefined是等同的,它们之间是可以进行隐式强制类型转换的,且是安全可靠的。善于利用还可以达到精炼代码的效果。

var a = doSomething();
if(a==null){
//……
}
//等同于
if(a===null||a===undefined){}

还有一些较为特殊的“假阳”情况需要我们特别注意的:

"0"==false;//true
false==0;//true
false=="";//true
false==[];//true
""==0;//true
""==[];//true
0==[];//true

还有一种较为特殊的情况就是:[]==![];//true.看起来有些不可思议,但是我们要知道其这最重要的地方不是==而是!。运算符!会调用ToBoolean规则,进行布尔值的强制类型转换,同时反转奇偶校验位。将![]变成false,如此就变成[]==false的比较了。

对象与非对象之间的相等比较

关于对象(对象/数组/函数)与非对象(字符串/数字/布尔值)之间的相等比较,ES5规范中的定义是:

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

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

对ToPrimitive的具体介绍可以参考这篇文章: 掌握js类型转换,先来学习js原始值转换的抽象操作 toPrimitive - 掘金 (juejin.cn)

所以说到这来我们对==和===应该有了一个较为清晰的印象,如果还没有也没有关系,我们只需要注意两点就可以有效的帮助我们在写代码的时候避免掉进强制类型转换的坑里;

  1. 如果两边的值中有true/false,尽量避免使用==
  2. 如果两边的值中有[],"",0,尽量避免使用==